Source code for Hlt2Conf.lines.charm.lc_to_ksh_kshhh

###############################################################################
# (c) Copyright 2022 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.                                       #
###############################################################################
"""The following lines are defined (charge conjugates are implied):

    Lambda_c+ -> p+ (KS0 -> pi+ pi-)
    Lambda_c+ -> p+ (KS0 -> pi+ pi-) K+ K-
    Lambda_c+ -> p+ (KS0 -> pi+ pi-) K- pi+
    Lambda_c+ -> p+ (KS0 -> pi+ pi-) K+ pi-
    Lambda_c+ -> p+ (KS0 -> pi+ pi-) pi+ pi-
    Lambda_c+ -> p+ (KS0 -> pi+ pi-) (KS0 -> pi+ pi-)

In the last line one KS0 is renamed as KL0 to avoid clash in having same
particles with two different containers.

Xic+ is reconstructed together with Lc+.

Both LL and DD modes of L0/KS0 are included as a separate lines.

Proponents: Miroslav Saur, Xiao-Rui Lyu, Ziyi Wang
TODO: Revisit all DD lines ; based on data move pion filters directly to KS builders ; remove decay descriptor from combine_lc_ks_ks_p when naming issue is solved
"""

import Functors as F
from Functors.math import in_range
from GaudiKernel.SystemOfUnits import GeV, MeV, mm
from Moore.config import register_line_builder
from Moore.lines import Hlt2Line
from RecoConf.reconstruction_objects import make_pvs
from ...standard_particles import (
    make_has_rich_long_pions, make_has_rich_long_kaons,
    make_has_rich_long_protons, make_has_rich_down_pions)
from ...algorithms_thor import ParticleCombiner, ParticleFilter
from .prefilters import charm_prefilters

all_lines = {}

###################
## track filters ##
###################


def filter_long_pions(pvs,
                      pt_min=300 * MeV,
                      p_min=2 * GeV,
                      mipchi2_min=5.,
                      pion_pidk_max=0.):
    """Filter long pions with P PT, MINIPCHI2CUT and pidp cuts."""
    cut = F.require_all(F.PT > pt_min, F.P > p_min,
                        F.MINIPCHI2CUT(IPChi2Cut=mipchi2_min, Vertices=pvs),
                        F.PID_K < pion_pidk_max)
    return ParticleFilter(make_has_rich_long_pions(), F.FILTER(cut))


def filter_long_pions_from_strange(pvs,
                                   pt_min=100 * MeV,
                                   p_min=2 * GeV,
                                   mipchi2_min=15.,
                                   pion_pidk_max=5.):
    """Filter long pions with P PT, MINIPCHI2CUT and pidp cuts."""
    cut = F.require_all(F.PT > pt_min, F.P > p_min,
                        F.MINIPCHI2CUT(IPChi2Cut=mipchi2_min, Vertices=pvs),
                        F.PID_K < pion_pidk_max)
    return ParticleFilter(make_has_rich_long_pions(), F.FILTER(cut))


def filter_long_kaons(pvs,
                      pt_min=400 * MeV,
                      p_min=3 * GeV,
                      mipchi2_min=5.,
                      pidk_min=3):
    """Filter long kaons with P PT, MINIPCHI2CUT and pidp cuts."""
    cut = F.require_all(F.PT > pt_min, F.P > p_min,
                        F.MINIPCHI2CUT(IPChi2Cut=mipchi2_min, Vertices=pvs),
                        F.PID_K > pidk_min)
    return ParticleFilter(make_has_rich_long_kaons(), F.FILTER(cut))


def filter_long_protons(pvs,
                        pt_min=400 * MeV,
                        p_min=9 * GeV,
                        mipchi2_min=6.,
                        pidp_min=3,
                        dllp_min=2):
    """Filter long protons with P PT, MINIPCHI2CUT and ProbNNp cuts.
    Defaults tuned for Lambda selection.
    """
    cut = F.require_all(F.PT > pt_min, F.P > p_min,
                        F.MINIPCHI2CUT(IPChi2Cut=mipchi2_min, Vertices=pvs),
                        F.PID_P > pidp_min, (F.PID_P - F.PID_K) > dllp_min)
    return ParticleFilter(make_has_rich_long_protons(), F.FILTER(cut))


def filter_down_pions(pt_min=300 * MeV, p_min=2 * GeV, pion_pidk_max=3.):
    """Filter downstream pions with P PT, and PID_P cuts."""
    cut = F.require_all(F.PT > pt_min, F.P > p_min, F.PID_K < pion_pidk_max)
    return ParticleFilter(make_has_rich_down_pions(), F.FILTER(cut))


##########################
## KS0 and L0 combiners ##
##########################


# TODO: apply BPVLTIME when will be working
def make_ks_ll(
        pions1,
        pions2,
        pvs,
        name='Charm_LcToKsHOrKsHHH_make_ks_ll_{hash}',
        comb_m_min=435 * MeV,
        comb_m_max=560 * MeV,
        m_min=450 * MeV,
        m_max=545 * MeV,
        comb_pt_min=300 * MeV,
        pt_min=350 * MeV,
        comb_p_min=3.5 * GeV,
        p_min=4.0 * GeV,
        doca_max=0.25 * mm,
        vchi2pdof_max=10.,
        bpvvdz_min=10 * mm,
        bpvfdchi2_min=25.,
        #bpvltime_min=1. * picosecond
):
    """Make KS -> pi+ pi- from long tracks."""
    comb_cut = F.require_all(
        F.MAXDOCACUT(doca_max), in_range(comb_m_min, F.MASS, comb_m_max),
        F.PT > comb_pt_min, F.P > comb_p_min)
    vertex_cut = F.require_all(
        in_range(m_min, F.MASS, m_max),
        F.PT > pt_min,
        F.P > p_min,
        F.CHI2DOF < vchi2pdof_max,
        F.BPVVDZ(pvs) > bpvvdz_min,
        F.BPVFDCHI2(pvs) > bpvfdchi2_min,
        #F.BPVLTIME(pvs) > bpvltime_min  #<-- not converging at all
    )
    return ParticleCombiner([pions1, pions2],
                            name=name,
                            DecayDescriptor="KS0 -> pi+ pi-",
                            CombinationCut=comb_cut,
                            CompositeCut=vertex_cut)


def make_ks_dd(
        pions1,
        pions2,
        pvs,
        name='Charm_LcToKsHOrKsHHH_make_ks_dd_{hash}',
        comb_m_min=435 * MeV,
        comb_m_max=560 * MeV,
        m_min=450 * MeV,
        m_max=545 * MeV,
        comb_pt_min=300 * MeV,
        pt_min=350 * MeV,
        sum_pt_min=500 * MeV,
        comb_p_min=4.5 * GeV,
        p_min=5.0 * GeV,
        doca_max=2 * mm,
        docachi2_max=30.,
        vchi2pdof_max=15.,
        #bpvltime_min=1. * picosecond
):
    """Make KS -> pi+ pi- from downstream tracks."""
    comb_cut = F.require_all(
        F.MAXDOCACUT(doca_max), F.MAXDOCACHI2CUT(docachi2_max),
        in_range(comb_m_min, F.MASS, comb_m_max), F.PT > comb_pt_min,
        F.SUM(F.PT) > sum_pt_min, F.P > comb_p_min)
    vertex_cut = F.require_all(
        in_range(m_min, F.MASS, m_max),
        F.PT > pt_min,
        F.P > p_min,
        F.CHI2DOF < vchi2pdof_max,
        #F.BPVLTIME(pvs) > bpvltime_min --- not converging
    )
    return ParticleCombiner([pions1, pions2],
                            name=name,
                            DecayDescriptor="KS0 -> pi+ pi-",
                            CombinationCut=comb_cut,
                            CompositeCut=vertex_cut)


##########################
## Lc+ / Xic+ combiners ##
##########################


def combine_lc_ks_h(
        protons,
        ks0,
        pvs,
        decay_descriptor,
        name='Charm_LcToKsHOrKsHHH_make_combine_lc_ks_h_{hash}',
        comb_m_min=2206 * MeV,
        comb_m_max=2540 * MeV,
        m_min=2216 * MeV,
        m_max=2527 * MeV,
        comb_pt_min=0.9 * GeV,
        pt_min=1.5 * GeV,
        sum_pt_min=1 * GeV,
        comb_p_min=15 * GeV,
        p_min=16 * GeV,
        doca_max=0.15 * mm,  #LL: 0.5 * mm ; DD: 1 * mm 
        vchi2pdof_max=6.,
        #bpvltime_min=0.2 * picosecond,
        bpvvdz_min=0. * mm,
        bpvfdchi2_min=5.,
        bpvipchi2_max=12.,  #9
        bpvdira_min=0.995,
        dz2_min=6. * mm):
    """Combine KS0 with protons to form Lc+ or Xic+.
    Make MASS, P, PT, SUM(PT), MAXDOCACUT cuts in the combination; MASS, P, PT,
    CHI2DOF, BPVVDZ, BPVFDCHI2, BPVIPCHI2, BPVDIRA cuts after the vertex fit.
    Cuts generally based on Run2 Turbo lines.
    """
    comb_cut = F.require_all(
        F.MAXSDOCACUT(doca_max), in_range(comb_m_min, F.MASS, comb_m_max),
        F.PT > comb_pt_min,
        F.SUM(F.PT) > sum_pt_min, F.P > comb_p_min)
    vertex_cut = F.require_all(
        in_range(m_min, F.MASS, m_max),
        F.PT > pt_min,
        F.P > p_min,
        F.CHI2DOF < vchi2pdof_max,
        #F.BPVLTIME(pvs) > bpvltime_min <-- currently does not work
        F.BPVVDZ(pvs) > bpvvdz_min,
        F.BPVFDCHI2(pvs) > bpvfdchi2_min,  #<-- to be checked
        F.BPVIPCHI2(pvs) < bpvipchi2_max,  #<-- to be checked
        F.BPVDIRA(pvs) > bpvdira_min,
        F.CHILD(2, F.END_VZ) - F.END_VZ > dz2_min)
    return ParticleCombiner([protons, ks0],
                            DecayDescriptor=decay_descriptor,
                            name=name,
                            CombinationCut=comb_cut,
                            CompositeCut=vertex_cut)


def combine_lc_ks_hhh(
        protons,
        ks0,
        particle1,
        particle2,
        pvs,
        decay_descriptor,
        name='Charm_LcToKsHOrKsHHH_make_combine_lc_ks_hhh_{hash}',
        comb_m_min=2216 * MeV,
        comb_m_max=2530 * MeV,
        m_min=2220 * MeV,
        m_max=2517 * MeV,
        comb_pt_min=0.9 * GeV,
        pt_min=1 * GeV,
        sum_pt_min=1 * GeV,
        comb_p_min=15 * GeV,
        p_min=16 * GeV,
        doca_max=1 * mm,
        vchi2pdof_max=5.,
        #bpvltime_min=0.2 * picosecond,
        bpvvdz_min=0.5 * mm,
        #bpvfdchi2_min=6.,
        bpvipchi2_max=12.,
        bpvdira_min=0.99,
        dz2_min=5. * mm):
    """Combine KS0 with protons and hh to form Lc+/Xic+.
    Make MASS, P, PT, SUM(PT), MAXDOCACUT cuts in the combination; MASS, P, PT,
    CHI2DOF, BPVVDZ, BPVFDCHI2, BPVIPCHI2, BPVDIRA cuts after the vertex fit.
    Cuts generally based on Run2 Turbo lines.
    """
    comb_cut = F.require_all(
        F.MAXSDOCACUT(doca_max), in_range(comb_m_min, F.MASS, comb_m_max),
        F.PT > comb_pt_min,
        F.SUM(F.PT) > sum_pt_min, F.P > comb_p_min)
    vertex_cut = F.require_all(
        in_range(m_min, F.MASS, m_max),
        F.PT > pt_min,
        F.P > p_min,
        F.CHI2DOF < vchi2pdof_max,
        #F.BPVLTIME(pvs) > bpvltime_min <-- currently does not work
        F.BPVVDZ(pvs) > bpvvdz_min,
        #F.BPVFDCHI2(pvs) > bpvfdchi2_min, <-- to be checked
        F.BPVIPCHI2(pvs) < bpvipchi2_max,  #<-- to be checked
        F.BPVDIRA(pvs) > bpvdira_min,
        F.CHILD(2, F.END_VZ) - F.END_VZ > dz2_min)
    return ParticleCombiner([protons, ks0, particle1, particle2],
                            DecayDescriptor=decay_descriptor,
                            name=name,
                            CombinationCut=comb_cut,
                            CompositeCut=vertex_cut)


def combine_lc_ks_ks_p(
        protons,
        ks01,
        ks02,
        pvs,
        decay_descriptor,
        name='Charm_LcToKsHOrKsHHH_make_combine_lc_ks_ks_h_{hash}',
        comb_m_min=2216 * MeV,
        comb_m_max=2530 * MeV,
        m_min=2220 * MeV,
        m_max=2517 * MeV,
        comb_pt_min=0.9 * GeV,
        pt_min=1 * GeV,
        sum_pt_min=1 * GeV,
        comb_p_min=15 * GeV,
        p_min=16 * GeV,
        doca_max=1.5 * mm,
        vchi2pdof_max=5.,
        #bpvltime_min=0.2 * picosecond,
        bpvvdz_min=0.5 * mm,
        #bpvfdchi2_min=6.,
        bpvipchi2_max=12.,
        bpvdira_min=0.99,
        dz_ks_min=5. * mm):
    """Combine KS0, KS0 with protons to form Lc+/Xic+.
    Make MASS, P, PT, SUM(PT), MAXDOCACUT cuts in the combination; MASS, P, PT,
    CHI2DOF, BPVVDZ, BPVFDCHI2, BPVIPCHI2, BPVDIRA cuts after the vertex fit.
    Cuts generally based on Run2 Turbo lines.
    """
    comb_cut = F.require_all(
        F.MAXSDOCACUT(doca_max), in_range(comb_m_min, F.MASS, comb_m_max),
        F.PT > comb_pt_min,
        F.SUM(F.PT) > sum_pt_min, F.P > comb_p_min)
    vertex_cut = F.require_all(
        in_range(m_min, F.MASS, m_max),
        F.PT > pt_min,
        F.P > p_min,
        F.CHI2DOF < vchi2pdof_max,
        #F.BPVLTIME(pvs) > bpvltime_min <-- currently does not work
        F.BPVVDZ(pvs) > bpvvdz_min,
        #F.BPVFDCHI2(pvs) > bpvfdchi2_min, <-- to be checked
        F.BPVIPCHI2(pvs) < bpvipchi2_max,  #<-- to be checked
        F.BPVDIRA(pvs) > bpvdira_min,
        F.CHILD(2, F.END_VZ) - F.END_VZ > dz_ks_min,
        F.CHILD(3, F.END_VZ) - F.END_VZ > dz_ks_min)
    return ParticleCombiner([protons, ks01, ks02],
                            DecayDescriptor=decay_descriptor,
                            name=name,
                            CombinationCut=comb_cut,
                            CompositeCut=vertex_cut)


###########################
## Hlt2 lines definition ##
###########################


[docs]@register_line_builder(all_lines) def lc_to_ks0p_ll_line(name="Hlt2Charm_LcpXicpToPpKs_LL", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_pions = filter_long_pions_from_strange(pvs) ks_ll = make_ks_ll(long_pions, long_pions, pvs) lc_ks0h = combine_lc_ks_h(long_protons, ks_ll, pvs, "[Lambda_c+ -> p+ KS0]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0h], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_ks0p_dd_line(name="Hlt2Charm_LcpXicpToPpKs_DD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) down_pions = filter_down_pions() ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0h = combine_lc_ks_h( long_protons, ks_dd, pvs, "[Lambda_c+ -> p+ KS0]cc", doca_max=1 * mm) return Hlt2Line( name=name, algs=charm_prefilters() + [ks_dd, lc_ks0h], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0kpkm_ll_line(name="Hlt2Charm_LcpXicpToPpKsKmKp_LL", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_kaons = filter_long_kaons(pvs) long_pions = filter_long_pions_from_strange(pvs) ks_ll = make_ks_ll(long_pions, long_pions, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_ll, long_kaons, long_kaons, pvs, "[Lambda_c+ -> p+ KS0 K- K+]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0kpkm_dd_line(name="Hlt2Charm_LcpXicpToPpKsKmKp_DD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_kaons = filter_long_kaons(pvs) down_pions = filter_down_pions() ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_dd, long_kaons, long_kaons, pvs, "[Lambda_c+ -> p+ KS0 K- K+]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_dd, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0kmpip_ll_line(name="Hlt2Charm_LcpXicpToPpKsKmPip_LL", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_kaons = filter_long_kaons(pvs) long_pions_ks = filter_long_pions_from_strange(pvs) long_pions = filter_long_pions(pvs) ks_ll = make_ks_ll(long_pions_ks, long_pions_ks, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_ll, long_kaons, long_pions, pvs, "[Lambda_c+ -> p+ KS0 K- pi+]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0kmpip_dd_line(name="Hlt2Charm_LcpXicpToPpKsKmPip_DD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_kaons = filter_long_kaons(pvs) long_pions = filter_long_pions(pvs) down_pions = filter_down_pions() ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_dd, long_kaons, long_pions, pvs, "[Lambda_c+ -> p+ KS0 K- pi+]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_dd, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0kppim_ll_line(name="Hlt2Charm_LcpXicpToPpKsKpPim_LL", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_pions_ks = filter_long_pions_from_strange(pvs) long_kaons = filter_long_kaons(pvs) long_pions = filter_long_pions(pvs) ks_ll = make_ks_ll(long_pions_ks, long_pions_ks, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_ll, long_kaons, long_pions, pvs, "[Lambda_c+ -> p+ KS0 K+ pi-]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0kppim_dd_line(name="Hlt2Charm_LcpXicpToPpKsKpPim_DD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_kaons = filter_long_kaons(pvs) long_pions = filter_long_pions(pvs) down_pions = filter_down_pions() ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_dd, long_kaons, long_pions, pvs, "[Lambda_c+ -> p+ KS0 K+ pi-]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_dd, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0pimpip_ll_line(name="Hlt2Charm_LcpXicpToPpKsPimPip_LL", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_pions_ks = filter_long_pions_from_strange(pvs) long_kaons = filter_long_kaons(pvs) long_pions = filter_long_pions(pvs) ks_ll = make_ks_ll(long_pions_ks, long_pions_ks, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_ll, long_kaons, long_pions, pvs, "[Lambda_c+ -> p+ KS0 pi- pi+]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lc_to_pks0pimpip_dd_line(name="Hlt2Charm_LcpXicpToPpKsPimPip_DD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_kaons = filter_long_kaons(pvs) long_pions = filter_long_pions(pvs) down_pions = filter_down_pions() ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0hhh = combine_lc_ks_hhh(long_protons, ks_dd, long_kaons, long_pions, pvs, "[Lambda_c+ -> p+ KS0 pi- pi+]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_dd, lc_ks0hhh], prescale=prescale)
[docs]@register_line_builder(all_lines) def lcp_to_pks0ks0_llll_line(name="Hlt2Charm_LcpXicpToPpKsKs_LLLL", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_pions = filter_long_pions_from_strange(pvs) ks_ll = make_ks_ll(long_pions, long_pions, pvs) lc_ks0ks0h = combine_lc_ks_ks_p(long_protons, ks_ll, ks_ll, pvs, "[Lambda_c+ -> p+ KS0 KS0]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0ks0h], prescale=prescale)
[docs]@register_line_builder(all_lines) def lcp_to_pks0ks0_lldd_line(name="Hlt2Charm_LcpXicpToPpKsKs_LLDD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) long_pions = filter_long_pions_from_strange(pvs) down_pions = filter_down_pions() ks_ll = make_ks_ll(long_pions, long_pions, pvs) ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0ks0h = combine_lc_ks_ks_p( long_protons, ks_ll, ks_dd, pvs, "[Lambda_c+ -> p+ KS0 KL0]cc" ) #One KS0 renamed as KL0 to avoid clash in having same particles with two different containers return Hlt2Line( name=name, algs=charm_prefilters() + [ks_ll, lc_ks0ks0h], prescale=prescale)
[docs]@register_line_builder(all_lines) def lcp_to_pks0ks0_dddd_line(name="Hlt2Charm_LcpXicpToPpKsKs_DDDD", prescale=1): pvs = make_pvs() long_protons = filter_long_protons(pvs) down_pions = filter_down_pions() ks_dd = make_ks_dd(down_pions, down_pions, pvs) lc_ks0ks0h = combine_lc_ks_ks_p(long_protons, ks_dd, ks_dd, pvs, "[Lambda_c+ -> p+ KS0 KS0]cc") return Hlt2Line( name=name, algs=charm_prefilters() + [ks_dd, lc_ks0ks0h], prescale=prescale)