Source code for Hlt2Conf.lines.qee.b_to_majolep_majo_to_leplep

###############################################################################
# (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration           #
#                                                                             #
# This software is distributed under the terms of the GNU General Public      #
# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
#                                                                             #
# In applying this licence, CERN does not waive the privileges and immunities #
# granted to it by virtue of its status as an Intergovernmental Organization  #
# or submit itself to any jurisdiction.                                       #
###############################################################################
"""
This defines HLT2 lines for B+(-)/B_c+(-) -> (X) mu N, N-> l l' nu where N is a neutral lepton
N has no lifetime hypothesis applied. Combination of the neutral lepton daugthers is limited between 200(to cut conversion)-7000 MeV
All leptons from B are Long. Daughters of the neutral lepton can be both Long and Downstream.
Loose and Tight lines have a different PID cut looseness on pions and electrons. Note, PID_E is applied to pions too, so there is a need for a no_brem added calibration.
Tight lines will hopefully have raw event information saved, specifically tracking infor.

List of lines (LL - Long, DD - downstream):

Hlt2QEE_BpToMajoMu_MajoToMuMu_LL_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mu mu, LL)
Hlt2QEE_BpToMajoMu_MajoToMuMu_DD_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mu mu, DD)
Hlt2QEE_BpToMajoMu_MajoToMuE_SS_LL_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mu+ e, LL)
Hlt2QEE_BpToMajoMu_MajoToMuE_SS_DD_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mu+ e, DD)
Hlt2QEE_BpToMajoMu_MajoToMuE_OS_LL_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mu- e, LL)
Hlt2QEE_BpToMajoMu_MajoToMuE_OS_DD_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mu- e, DD)

Hlt2QEE_BpToMajoE_MajoToMuMu_LL_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu mu, LL)
Hlt2QEE_BpToMajoE_MajoToMuMu_DD_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu mu, DD)
Hlt2QEE_BpToMajoE_MajoToMuE_SS_LL_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu+ e, LL)
Hlt2QEE_BpToMajoE_MajoToMuE_SS_DD_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu+ e, DD)
Hlt2QEE_BpToMajoE_MajoToMuE_OS_LL_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu- e, LL)
Hlt2QEE_BpToMajoE_MajoToMuE_OS_DD_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu- e, DD)

Contact: Louis Henry, louis.henry@cern.ch

"""
from Moore.config import register_line_builder
from Moore.lines import Hlt2Line
from GaudiKernel.SystemOfUnits import MeV, GeV

from Hlt2Conf.standard_particles import (make_long_muons,
                                         make_long_electrons_with_brem)
from Functors.math import in_range
from Hlt2Conf.algorithms_thor import ParticleCombiner

from Hlt2Conf.lines.qee.qee_builders import hnl_prefilter, make_filter_tracks
import Functors as F

all_lines = {}
_HNL_MONITORING_VARIABLES = ("pt", "eta", "vchi2", "ipchi2", "n_candidates")

defaultCutDict = {
    "AllTracks": {
        "P": 2000. * MeV
    },
    "Children": {
        "MINIPCHI2PV": 200
    },
    "Bachelor": {},
    "Child1": {},
    "Child2": {},
    "Majo": {
        "FDCHI2MIN": 2000,
        "MASS": [0.2 * GeV, 6. * GeV],
        "MINIPCHI2PV": 20,
    },
    "B": {
        "MASS": [4. * GeV, 7. * GeV]
    }
}
child_muon_pidCut = [F.ISMUON, F.PID_MU > 0.]
bach_muon_pidCut = [F.PID_MU > 2]

electron_pidCut = [F.PID_E > -2]

particle_makers = {"e": make_long_electrons_with_brem, "mu": make_long_muons}


#Build a cut from a list of functors
def buildFromList(cutList):
    toReturn = F.ALL
    for i in range(0, len(cutList)):
        toReturn &= cutList[i]
    return toReturn


#Build a cut from a dictionnary
def build_cut_on_track(cutDict, pvs):
    listOfFunctors = []
    for cut, cutVal in cutDict.items():
        if cut == "PIDCuts":
            listOfFunctors += cutVal
        elif cut == "MASS":
            listOfFunctors.append(in_range(cutVal[0], F.MASS, cutVal[1]))
        else:
            functors = {
                "P": F.P,
                "PT": F.PT,
                "MINIPCHI2PV": F.MINIPCHI2(pvs()),
                "FDCHI2MIN": F.BPVFDCHI2(pvs())
            }
            listOfFunctors.append(functors[cut] > cutVal)
    #Build the cut
    return buildFromList(listOfFunctors)


def build_combination_cut(cutDict, pvs):
    listOfFunctors = []
    for cut, cutVal in cutDict.items():
        if cut == "MASS":
            listOfFunctors.append(in_range(cutVal[0], F.MASS, cutVal[1]))
    #Build the cut
    return buildFromList(listOfFunctors)


def builder_BToMajoL_line(name,
                          bachelorName,
                          children,
                          cutDict={},
                          prescale=1,
                          persistreco=True):
    from RecoConf.reconstruction_objects import make_pvs
    pvs = make_pvs
    # Interpret name into a B decay
    majoDecayDescr = "KS0 -> " + children[0] + "+ " + children[1] + "-"
    recoMode = "LL" if "LL" in name else "DD"
    # Build the cut dictionnaries
    # Add all default cuts
    for key1, val1 in defaultCutDict.items():
        if key1 not in cutDict:
            cutDict[key1] = val1
        else:
            for key2, val2 in val1.items():
                if key2 not in cutDict[key1]:
                    cutDict[key1][key2] = val2
    # Apply Children and AllTracks cuts
    for key, val in cutDict["Children"].items():
        for child in ["Child1", "Child2"]:
            if key not in cutDict[child]:
                cutDict[child][key] = val
    for key, val in cutDict["AllTracks"].items():
        for recTrack in ["Bachelor", "Child1", "Child2"]:
            if key not in cutDict[recTrack]:
                cutDict[recTrack][key] = val
    # Apply PID cuts
    cutDict["Bachelor"][
        "PIDCuts"] = bach_muon_pidCut if bachelorName == "mu" else electron_pidCut
    cutDict["Child1"]["PIDCuts"] = child_muon_pidCut if children[
        0] == "mu" else electron_pidCut
    cutDict["Child2"]["PIDCuts"] = child_muon_pidCut if children[
        1] == "mu" else electron_pidCut

    # Make particles and decays
    firstChild = make_filter_tracks(
        make_particles=particle_makers[children[0]],
        name="majo_" + children[0] + "_{hash}",
        additionalCuts=build_cut_on_track(cutDict["Child1"], pvs))
    secondChild = make_filter_tracks(
        make_particles=particle_makers[children[1]],
        name="majo_" + children[1] + "_{hash}",
        additionalCuts=build_cut_on_track(cutDict["Child2"], pvs))
    majo = ParticleCombiner(
        [firstChild, secondChild],
        name="Majo2" + children[0] + children[1] + "_" + recoMode + "_{hash}",
        DecayDescriptor=majoDecayDescr,
        CombinationCut=build_combination_cut(cutDict["Majo"], pvs),
        CompositeCut=build_cut_on_track(cutDict["Majo"], pvs))
    #Bachelor
    bachelor = make_filter_tracks(
        make_particles=particle_makers[bachelorName],
        name="bachelor_{hash}",
        additionalCuts=build_cut_on_track(cutDict["Bachelor"], pvs))
    #Decay
    b2LN = ParticleCombiner(
        [majo, bachelor],
        name=name + "_{hash}",
        DecayDescriptor='[B+ -> KS0 ' + bachelorName + '+]cc',
        CombinationCut=build_combination_cut(cutDict["B"], pvs),
        CompositeCut=build_cut_on_track(cutDict["B"], pvs))

    # Define monitoring
    from SelAlgorithms.monitoring import monitor, histogram_1d
    mons = []
    for ptName, pt in [("Child1", firstChild), ("Child2", secondChild),
                       ("N", majo)]:
        hists = []
        for var, varName, varRange in [(F.P, "P", (0, 10000)),
                                       (F.PT, "PT", (0, 2000)),
                                       (F.MINIPCHI2(pvs()), "MINIPCHI2",
                                        (0, 1000))]:
            hists.append(
                histogram_1d(
                    name=f"/{name}/{ptName}_{varName}",
                    title=f"/{name}/{ptName}_{varName}",
                    label=varName,
                    functor=var,
                    bins=1000,
                    range=varRange))

        mons.append(
            monitor(
                name=f"Monitor__{name}__{ptName}", data=pt, histograms=hists))
        if ptName == "Child1" or ptName == "Child2":
            child_specific_hists = []
            for var, varName, varRange in [(F.CHI2DOF, "TRCHI2NDOF", (0, 50)),
                                           (F.GHOSTPROB, "GhostProb", (0, 1))]:

                child_specific_hists.append(
                    histogram_1d(
                        name=f"Monitor__{name}__{ptName}_{varName}",
                        title=f"{name}__{ptName}_{varName}",
                        label=varName,
                        functor=var,
                        bins=100,
                        range=varRange))
            mons.append(
                monitor(
                    name=f"Monitor__{name}__{ptName}__child_specific",
                    data=pt,
                    histograms=child_specific_hists))
    return Hlt2Line(
        name=name,
        algs=hnl_prefilter(require_GEC=True) + [b2LN] + mons,
        prescale=prescale,
        monitoring_variables=_HNL_MONITORING_VARIABLES)


for bachelor in ["mu", "e"]:
    for children in [("mu", "mu"), ("mu", "e"), ("e", "mu"), ("e", "e")]:
        name = f"Hlt2QEE_BpToMajo{bachelor.capitalize()}_MajoTo{children[0].capitalize()}{children[1].capitalize()}_LL_Tight"
        if children != ("e", "e"):

            @register_line_builder(all_lines)
            def make_line(name=name, bachelor=bachelor, children=children):
                return builder_BToMajoL_line(name, bachelor, children)
        else:

[docs] @register_line_builder(all_lines) def make_line(name=name, bachelor=bachelor, children=children): return builder_BToMajoL_line( name, bachelor, children, cutDict={ "Children": { "MINIPCHI2PV": 500, "PT": 500 * MeV }, "Majo": { "FDCHI2MIN": 3000, }, })