Source code for Hlt2Conf.lines.charm.d0_to_hhhh

###############################################################################
# (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.                                       #
###############################################################################
"""Definition of D0 -> h- h+ h- h+ HLT2 lines.

The D0 final states built are:
  1. D0 -> K-  pi+ pi+ pi- and its charge conjugate
  2. D0 -> K+  pi- pi+ pi- and its charge conjugate
  3. D0 -> K-  K+  pi+ pi-
  4. D0 -> pi- pi+ pi+ pi-
  5. D0 -> K-  K+  K-  pi+ and its charge conjugate
  6. D0 -> K-  K+  K+  pi- and its charge conjugate

Each of the final states is tagged in the following ways:
  1. D*(2010)+ -> D0 pi+ and its charge conjugate
  2. B- -> D0 mu- and its charge conjugate
  3. B0 -> D*(2010)- mu+ and its charge conjugate

The charge conjugates of D0 -> h- h+ pi+ pi- are not reconstructed to avoid
duplication of candidates. Therefore, the taggers employ modified (unphysical)
charge conjugates, e.g. D*(2010)- -> D0 pi- and B+ -> D0 mu+.

TODO: add HLT1 filters
"""
import Functors as F
from Functors.math import in_range
from GaudiKernel.SystemOfUnits import MeV, mm, GeV
from Moore.config import register_line_builder
from Moore.lines import Hlt2Line
from RecoConf.reconstruction_objects import make_pvs
from Hlt2Conf.algorithms_thor import (ParticleCombiner, ParticleFilter)
from Hlt2Conf.standard_particles import (make_has_rich_long_pions,
                                         make_has_rich_long_kaons)
from .particle_properties import _PION_M, _KAON_M
from .prefilters import charm_prefilters
from .taggers import make_dstars, make_tagging_muons, make_sl_bs, make_dt_bs

all_lines = {}


def make_charm_pions():
    """Return pions maker for D0 decay."""
    return ParticleFilter(
        make_has_rich_long_pions(),
        F.FILTER(
            F.require_all(
                F.MINIPCHI2CUT(IPChi2Cut=3.5, Vertices=make_pvs()),
                F.PT > 250 * MeV,
                # p_min=5 * GeV,
                # trchi2dof_max=4,
                # trg_ghost_prob_max=0.5,
                F.PID_K < 5.,
            ), ),
    )


def make_charm_kaons():
    """Return kaons maker for D0 decay."""
    return ParticleFilter(
        make_has_rich_long_kaons(),
        F.FILTER(
            F.require_all(
                F.MINIPCHI2CUT(IPChi2Cut=3.5, Vertices=make_pvs()),
                F.PT > 250 * MeV,
                # p_min=5 * GeV,
                # trchi2dof_max=4,
                # trg_ghost_prob_max=0.5,
                F.PID_K > 5.,
            ), ),
    )


# These tracks have the same selection as the D0->hh ones.
# Shall we somehow use the same selection?
def make_charm_pions_for_b():
    return ParticleFilter(
        make_has_rich_long_pions(),
        F.FILTER(
            F.require_all(
                F.MINIPCHI2CUT(IPChi2Cut=9., Vertices=make_pvs()),
                F.PID_K < 0.,
                F.PT > 200 * MeV,
                F.P > 2 * GeV,
            ), ),
    )


def make_charm_kaons_for_b():
    return ParticleFilter(
        make_has_rich_long_kaons(),
        F.FILTER(
            F.require_all(
                F.MINIPCHI2CUT(IPChi2Cut=9., Vertices=make_pvs()),
                F.PID_K > 0.,
                F.PT > 200 * MeV,
                F.P > 2 * GeV,
            ), ),
    )


def make_dzeros(particle1,
                particle2,
                particle3,
                particle4,
                descriptor,
                m3=_PION_M,
                m4=_PION_M,
                for_b=False):
    """Return D0 maker with selection tailored for four-body hadronic final
    states.

    Args:
        particles (list of DataHandles): Input particles used in the
                                         combination.
        descriptor (string): Decay descriptor to be reconstructed.
        for_b (bool, optional): Sets whether the requirements should be tuned
                                to prompt or SL decays. Defaults to False.
    """
    if descriptor == '[D0 -> K- pi- pi+ pi+]cc':
        name = "Charm_D0ToHHHH_BuilderD0ToKmPimPipPip_{hash}"
    elif descriptor == '[D0 -> K+ pi- pi- pi+]cc':
        name = "Charm_D0ToHHHH_BuilderD0ToKpPimPimPip_{hash}"
    elif descriptor == 'D0 -> K- K+ pi- pi+':
        name = "Charm_D0ToHHHH_BuilderD0ToKmKpPimPip_{hash}"
    elif descriptor == 'D0 -> pi- pi- pi+ pi+':
        name = "Charm_D0ToHHHH_BuilderD0ToPimPimPipPip_{hash}"
    elif descriptor == '[D0 -> K- K- K+ pi+]cc':
        name = "Charm_D0ToHHHH_BuilderD0ToKmKmKpPip_{hash}"
    elif descriptor == '[D0 -> K- K+ K+ pi-]cc':
        name = "Charm_D0ToHHHH_BuilderD0ToKmKpKpPim_{hash}"
    else:
        raise RuntimeError(
            f"charm.d0_to_hhhh.make_dzeros called with unsupported decay descriptor '{descriptor}'"
        )
    if for_b:
        name = f"{name}_ForSL"

    comb_m_max = (1940 if for_b else 2100) * MeV

    combination_cut = F.require_all(
        in_range((1790 if for_b else 1700) * MeV, F.MASS, comb_m_max),
        F.PT > (1700 if for_b else 1900) * MeV,
        F.MAXSDOCACUT(0.1 * mm),
    )
    if not for_b:
        combination_cut &= F.require_all(
            F.P > 25 * GeV,
            F.MAX(F.PT) > 1200 * MeV)  # could be added if needed

    composite_cut = F.require_all(
        in_range((1800 if for_b else 1790) * MeV, F.MASS,
                 (1930 if for_b else 2010) * MeV),
        F.PT > (1800 if for_b else 2500) * MeV,
        F.CHI2DOF < (6. if for_b else 12.),
        F.BPVFDCHI2(make_pvs()) > (100. if for_b else 25.),
        # F.BPVLTIME(make_pvs()) > 0.1 * ps, # TODO once we know our detector better
        F.BPVDIRA(make_pvs()) > (0.99 if for_b else 0.9998))
    if not for_b:
        composite_cut &= F.P > 30 * GeV

    return ParticleCombiner(
        [particle1, particle2, particle3, particle4],
        DecayDescriptor=descriptor,
        name=name,
        Combination12Cut=F.require_all(
            F.MASS < comb_m_max - m3 - m4,
            F.MAXSDOCACUT(0.1 * mm),
        ),
        Combination123Cut=F.require_all(
            F.MASS < comb_m_max - m4,
            F.MAXSDOCACUT(0.1 * mm),
        ),
        CombinationCut=combination_cut,
        CompositeCut=composite_cut,
    )


##############################################################################
# Prompt D*+ -> D0 pi+ lines
##############################################################################


[docs]@register_line_builder(all_lines) def dstarp2dzeropip_dzero2kmpippimpip_line( name='Hlt2Charm_DstpToD0Pip_D0ToKmPimPipPip', prescale=1): kaons = make_charm_kaons() pions = make_charm_pions() dzeros = make_dzeros(kaons, pions, pions, pions, '[D0 -> K- pi- pi+ pi+]cc') dstars = make_dstars( dzeros, self_conjugate_d0_decay=False, d0_name="D0ToKPiPiPi") return Hlt2Line( name=name, algs=charm_prefilters() + [dzeros, dstars], prescale=prescale)
[docs]@register_line_builder(all_lines) def dstarp2dzeropip_dzero2kppimpippim_line( name='Hlt2Charm_DstpToD0Pip_D0ToKpPimPimPip', prescale=1): kaons = make_charm_kaons() pions = make_charm_pions() dzeros = make_dzeros(kaons, pions, pions, pions, '[D0 -> K+ pi- pi- pi+]cc') dstars = make_dstars( dzeros, self_conjugate_d0_decay=False, d0_name="D0ToKPiPiPiWS") return Hlt2Line( name=name, algs=charm_prefilters() + [dzeros, dstars], prescale=prescale)
[docs]@register_line_builder(all_lines) def dstarp2dzeropip_dzero2kmkppimpip_line( name='Hlt2Charm_DstpToD0Pip_D0ToKmKpPimPip', prescale=1): kaons = make_charm_kaons() pions = make_charm_pions() dzeros = make_dzeros(kaons, kaons, pions, pions, 'D0 -> K- K+ pi- pi+') dstars = make_dstars( dzeros, self_conjugate_d0_decay=True, d0_name="D0ToKKPiPi") return Hlt2Line( name=name, algs=charm_prefilters() + [dzeros, dstars], prescale=prescale)
[docs]@register_line_builder(all_lines) def dstarp2dzeropip_dzero2kmkpkmpip_line( name='Hlt2Charm_DstpToD0Pip_D0ToKmKmKpPip', prescale=1): kaons = make_charm_kaons() pions = make_charm_pions() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K- K+ pi+]cc', m3=_KAON_M) dstars = make_dstars( dzeros, self_conjugate_d0_decay=False, d0_name="D0ToKKKPi") return Hlt2Line( name=name, algs=charm_prefilters() + [dzeros, dstars], prescale=prescale)
[docs]@register_line_builder(all_lines) def dstarp2dzeropip_dzero2kpkmkppim_line( name='Hlt2Charm_DstpToD0Pip_D0ToKmKpKpPim', prescale=1): kaons = make_charm_kaons() pions = make_charm_pions() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K+ K+ pi-]cc', m3=_KAON_M) dstars = make_dstars( dzeros, self_conjugate_d0_decay=False, d0_name="D0ToKKKPiWS") return Hlt2Line( name=name, algs=charm_prefilters() + [dzeros, dstars], prescale=prescale)
[docs]@register_line_builder(all_lines) def dstarp2dzeropip_dzero2pimpippimpip_line( name='Hlt2Charm_DstpToD0Pip_D0ToPimPimPipPip', prescale=1): pions = make_charm_pions() dzeros = make_dzeros(pions, pions, pions, pions, 'D0 -> pi- pi- pi+ pi+') dstars = make_dstars( dzeros, self_conjugate_d0_decay=True, d0_name="D0ToPiPiPiPi") return Hlt2Line( name=name, algs=charm_prefilters() + [dzeros, dstars], prescale=prescale)
############################################################################### # Semi-leptonic singly tagged lines B -> D0 mu- ###############################################################################
[docs]@register_line_builder(all_lines) def b2d0mu_dzero2kpipipi_line(name='Hlt2Charm_BToD0MumX_D0ToKmPimPipPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, pions, pions, pions, '[D0 -> K- pi- pi+ pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_sl_bs(dzeros, muons, self_conjugate_d0_decay=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2d0mu_dzero2kkpipi_line(name='Hlt2Charm_BToD0MumX_D0ToKmKpPimPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, pions, pions, 'D0 -> K- K+ pi- pi+', for_b=True) muons = make_tagging_muons() bs = make_sl_bs(dzeros, muons, self_conjugate_d0_decay=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2d0mu_dzero2pipipipi_line(name='Hlt2Charm_BToD0MumX_D0ToPimPimPipPip', prescale=1): pions = make_charm_pions_for_b() dzeros = make_dzeros( pions, pions, pions, pions, 'D0 -> pi- pi- pi+ pi+', for_b=True) muons = make_tagging_muons() bs = make_sl_bs(dzeros, muons, self_conjugate_d0_decay=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2d0mu_dzero2kkkpi_line(name='Hlt2Charm_BToD0MumX_D0ToKmKmKpPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K- K+ pi+]cc', m3=_KAON_M, for_b=True) muons = make_tagging_muons() bs = make_sl_bs(dzeros, muons, self_conjugate_d0_decay=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2d0mu_dzero2kpipipiws_line(name='Hlt2Charm_BToD0MumX_D0ToKpPimPimPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, pions, pions, pions, '[D0 -> K+ pi- pi- pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_sl_bs(dzeros, muons, self_conjugate_d0_decay=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2d0mu_dzero2kkkpiws_line(name='Hlt2Charm_BToD0MumX_D0ToKmKpKpPim', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K+ K+ pi-]cc', m3=_KAON_M, for_b=True) muons = make_tagging_muons() bs = make_sl_bs(dzeros, muons, self_conjugate_d0_decay=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, bs], prescale=prescale)
############################################################################### # Doubly tagged B -> D*(2010)+ mu+ lines (for combinatorial bkg) ###############################################################################
[docs]@register_line_builder(all_lines) def b2dstarmu_dzero2kpipipi_line(name='Hlt2Charm_BToDstpMumX_D0ToKmPimPipPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, pions, pions, pions, '[D0 -> K- pi- pi+ pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKmPimPipPip', same_sign=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmu_dzero2kpipipiws_line( name='Hlt2Charm_BToDstpMumX_D0ToKpPimPimPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, pions, pions, pions, '[D0 -> K+ pi- pi- pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKpPimPimPip', same_sign=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmu_dzero2pipipipi_line( name='Hlt2Charm_BToDstpMumX_D0ToPimPimPipPip', prescale=1): pions = make_charm_pions_for_b() dzeros = make_dzeros( pions, pions, pions, pions, 'D0 -> pi- pi- pi+ pi+', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=True, d0_name='D0ToPimPimPipPip', same_sign=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmu_dzero2kkpipi_line(name='Hlt2Charm_BToDstpMumX_D0ToKmKpPimPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, pions, pions, 'D0 -> K- K+ pi- pi+', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=True, d0_name='D0ToKmKpPimPip', same_sign=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmu_dzero2kkkpi_line(name='Hlt2Charm_BToDstpMumX_D0ToKmKmKpPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K- K+ pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKmKmKpPip', same_sign=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmu_dzero2kkkpiws_line(name='Hlt2Charm_BToDstpMumX_D0ToKmKpKpPim', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K+ K+ pi-]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKmKpKpPim', same_sign=False) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
############################################################################### # Same-sign doubly tagged B -> D*(2010)+ mu+ lines (for combinatorial bkg) ###############################################################################
[docs]@register_line_builder(all_lines) def b2dstarmuWS_dzero2kpipipi_line( name='Hlt2Charm_BToDstpMupX_D0ToKmPimPipPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, pions, pions, pions, '[D0 -> K- pi- pi+ pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKmPimPipPip', same_sign=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmuWS_dzero2kpipipiws_line( name='Hlt2Charm_BToDstpMupX_D0ToKpPimPimPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, pions, pions, pions, '[D0 -> K+ pi- pi- pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKpPimPimPip', same_sign=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmuWS_dzero2pipipipi_line( name='Hlt2Charm_BToDstpMupX_D0ToPimPimPipPip', prescale=1): pions = make_charm_pions_for_b() dzeros = make_dzeros( pions, pions, pions, pions, 'D0 -> pi- pi- pi+ pi+', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=True, d0_name='D0ToPimPimPipPip', same_sign=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmuWS_dzero2kkpipi_line(name='Hlt2Charm_BToDstpMupX_D0ToKmKpPimPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, pions, pions, 'D0 -> K- K+ pi- pi+', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=True, d0_name='D0ToKmKpPimPip', same_sign=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmuWS_dzero2kkkpi_line(name='Hlt2Charm_BToDstpMupX_D0ToKmKmKpPip', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K- K+ pi+]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKmKmKpPip', same_sign=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)
[docs]@register_line_builder(all_lines) def b2dstarmuWS_dzero2kkkpiws_line(name='Hlt2Charm_BToDstpMupX_D0ToKmKpKpPim', prescale=1): kaons = make_charm_kaons_for_b() pions = make_charm_pions_for_b() dzeros = make_dzeros( kaons, kaons, kaons, pions, '[D0 -> K- K+ K+ pi-]cc', for_b=True) muons = make_tagging_muons() bs = make_dt_bs( dzeros, muons, self_conjugate_d0_decay=False, d0_name='D0ToKmKpKpPim', same_sign=True) return Hlt2Line( name=name, algs=charm_prefilters() + [muons, dzeros, bs], prescale=prescale)