Source code for Hlt2Conf.lines.trackeff.Velo2Long_B2JpsiK_ElectronProbe

##############################################################################
# (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.                                       #
###############################################################################
"""
HLT2 line using B->K+Jpsi(->ee) decays, where one of the electrons
      is a VELO track (the probe):
    - The new Velo2LongB2jpsiKTrackEff_VeloTrackEffFilter is used to fix
      the determination of the best PV and apply a series of cuts using
      the redefined best PV.
    - An equivalent line with two muons is available.
    - Contacts: guillaume.pietryzk@cern.ch and laurent.dufour@cern.ch.
"""
from PyConf import configurable
from Moore.config import register_line_builder
from Moore.lines import Hlt2Line

from Hlt2Conf.standard_particles import (
    make_long_electrons_with_brem,
    make_long_kaons,
    _make_particles,
    get_all_track_selector,
)

from RecoConf.reconstruction_objects import make_pvs, upfront_reconstruction
from GaudiKernel.SystemOfUnits import MeV, GeV, mm
import Functors as F
from Hlt2Conf.algorithms_thor import ParticleFilter, ParticleCombiner, ParticleContainersMerger
from PyConf.Algorithms import (
    FunctionalChargedProtoParticleMaker,
    Velo2LongB2jpsiKTrackEff_VeloTrackEffFilter,
)
from RecoConf.hlt2_tracking import make_hlt2_tracks_without_UT
from RecoConf.event_filters import require_pvs

all_lines = {}


@configurable
def make_b2Jpsiee(
        particles,
        descriptors,
        pvs,
        min_B_mass=1000.0 * MeV,
        max_B_mass=7000.0 * MeV,
        doca_max=0.15 * mm,
        bestchild_pt_min=2 * GeV,
        tag_comb_pt2_min=1.0 * GeV * 1.0 * GeV,
        tag_comb_m2_min=1.5 * GeV * 1.5 * GeV,
):
    """Make the B+ -> (J/psi -> e+ e-) K+  candidate"""

    combination_code = F.require_all(
        F.MASS > min_B_mass,
        F.MASS < max_B_mass,
        F.MAXSDOCACUT(doca_max),
        ((F.CHILD(1, F.CHILD(1, F.PX)) + F.CHILD(2, F.PX)) *
         (F.CHILD(1, F.CHILD(1, F.PX)) + F.CHILD(2, F.PX)) +
         (F.CHILD(1, F.CHILD(1, F.PY)) + F.CHILD(2, F.PY)) *
         (F.CHILD(1, F.CHILD(1, F.PY)) + F.CHILD(2, F.PY))) > tag_comb_pt2_min,
        (F.CHILD(1, F.CHILD(1, F.MASS)) * F.CHILD(1, F.CHILD(1, F.MASS)) +
         F.CHILD(2, F.MASS) * F.CHILD(2, F.MASS) +
         2.0 * F.CHILD(1, F.CHILD(1, F.ENERGY)) * F.CHILD(2, F.ENERGY) - 2.0 *
         (F.CHILD(1, F.CHILD(1, F.PX)) * F.CHILD(2, F.PX) +
          F.CHILD(1, F.CHILD(1, F.PY)) * F.CHILD(2, F.PY) +
          F.CHILD(1, F.CHILD(1, F.PZ)) * F.CHILD(2, F.PZ))) > tag_comb_m2_min,

        # this line is candidate for F.FLATTEN, or some NINTREE
        ((F.CHILD(1, F.CHILD(1, F.PT)) > bestchild_pt_min) |
         (F.CHILD(2, F.PT) > bestchild_pt_min)))

    # some additional cuts
    vertex_code = F.require_all(F.MASS > min_B_mass, F.MASS < max_B_mass,
                                F.CHI2DOF < 9)

    return ParticleCombiner(
        particles,
        DecayDescriptor=descriptors,
        CombinationCut=combination_code,
        CompositeCut=vertex_code,
    )


@configurable
def make_TagAndProbeJpsi(
        particles,
        descriptors,
        pvs,
        min_Jpsi_mass=250.0 * MeV,
        max_Jpsi_mass=4500.0 * MeV,
        doca_max=0.15 * mm,
):

    combination_code = F.require_all(
        F.MAXSDOCACUT(doca_max),
        F.CHILD(1, F.PT) > 450 * MeV,  # force the first track to be long,
        F.CHILD(2, F.PT) < 450 * MeV  # force the second track to be velo
    )

    vertex_code = F.require_all(F.MASS > min_Jpsi_mass, F.MASS < max_Jpsi_mass,
                                F.CHI2DOF < 9)

    return ParticleCombiner(
        particles,
        DecayDescriptor=descriptors,
        CombinationCut=combination_code,
        CompositeCut=vertex_code,
        AllowDiffInputsForSameIDChildren=True)


@configurable
def filter_TagKaons(
        particles,
        pvs,
        min_p=7.5 * GeV,
        min_pt=0.5 * GeV,
        max_trchi2dof=5.0,
        min_ipchi2=16.0,
        min_pid=5.0,
        min_eta=2.0,
        max_eta=4.8,
):

    code = F.require_all(F.PT > min_pt, F.CHI2DOF < max_trchi2dof,
                         F.MINIPCHI2(pvs) > min_ipchi2, F.PID_K > min_pid,
                         F.ETA > min_eta, F.ETA < max_eta, F.P > min_p)

    return ParticleFilter(
        particles,
        F.FILTER(code),
    )


@configurable
def filter_TagElectrons(
        particles,
        pvs,
        min_pt=0.8 * GeV,
        max_trchi2dof=5.0,
        min_eta=2.0,
        max_eta=4.8,
        min_p=7.5 * GeV,
        min_ipchi2=16.0,
        min_pid=5.0,
):

    code = F.require_all(F.PT > min_pt, F.CHI2DOF < max_trchi2dof,
                         F.MINIPCHI2(pvs) > min_ipchi2, F.PID_E > min_pid,
                         F.P > min_p, F.ETA < max_eta, F.ETA > min_eta)

    return ParticleFilter(
        particles,
        F.FILTER(code),
    )


@configurable
def velo_track_electron_protos():
    my_velo_tracks = make_hlt2_tracks_without_UT(
        light_reco=True, use_pr_kf=True)['Velo']['v1']
    charged_protos = FunctionalChargedProtoParticleMaker(
        Inputs=[my_velo_tracks], Code=F.require_all(~F.TRACKISVELOBACKWARD))

    return charged_protos.Output


@configurable
def make_velo_track_electron_particles():
    return _make_particles(
        species="electron",
        make_protoparticles=velo_track_electron_protos,
        get_track_selector=get_all_track_selector)


@configurable
def filter_ProbeElectrons(
        particles,
        pvs,
        min_ip=0.1 * mm,
        min_eta=2.0,
        max_eta=4.8,
):

    code = F.require_all(F.ETA > min_eta, F.ETA < max_eta,
                         F.MINIP(pvs) > min_ip)

    return ParticleFilter(
        particles,
        F.FILTER(code),
    )


@configurable
def create_jpsi_particles_all_charge_combinations(tag_electrons,
                                                  probe_electrons, pvs):
    jpsi_particle_combinations = [
        make_TagAndProbeJpsi(
            particles=[tag_electrons, probe_electrons],
            descriptors="J/psi(1S) -> e+ e-",
            pvs=pvs),
        make_TagAndProbeJpsi(
            particles=[tag_electrons, probe_electrons],
            descriptors="J/psi(1S) -> e- e-",
            pvs=pvs),
        make_TagAndProbeJpsi(
            particles=[tag_electrons, probe_electrons],
            descriptors="J/psi(1S) -> e+ e+",
            pvs=pvs),
        make_TagAndProbeJpsi(
            particles=[tag_electrons, probe_electrons],
            descriptors="J/psi(1S) -> e- e+",
            pvs=pvs)
    ]

    return jpsi_particle_combinations


[docs]@register_line_builder(all_lines) @configurable def TrackEff_Bu2Jpsi2eeK_TagAndProbe_line( name="Hlt2TrackEff_Velo2Long_B2JpsiK_ElectronProbe_VELO", prescale=1): pvs = make_pvs() probe_electrons = filter_ProbeElectrons( particles=make_velo_track_electron_particles(), pvs=pvs) tag_electrons = filter_TagElectrons( particles=make_long_electrons_with_brem(), pvs=pvs) my_jpsi_combinations = create_jpsi_particles_all_charge_combinations( tag_electrons=tag_electrons, probe_electrons=probe_electrons, pvs=pvs) tagandprobe_jpsi = ParticleContainersMerger( my_jpsi_combinations, name='TrackEffEle_Jpsi_combinations_{hash}') tag_kaons = filter_TagKaons(particles=make_long_kaons(), pvs=pvs) bplus = make_b2Jpsiee( particles=[tagandprobe_jpsi, tag_kaons], descriptors="[B+ -> J/psi(1S) K+]cc", pvs=pvs) TrackEffFilterCode = Velo2LongB2jpsiKTrackEff_VeloTrackEffFilter( Input=bplus, InputPVs=pvs, MassConstrainedMassMin=4800. * MeV, MassConstrainedMassMax=6000. * MeV, MassConstrainedPtMin=2500. * MeV, ProbeMassConstrainedPMin=5000. * MeV, ProbeMassConstrainedPMax=140000. * MeV, ProbeMassConstrainedPtMin=350. * MeV, PVConstrainedDeltaMassMin=1000. * MeV, PVConstrainedDeltaMassMax=3000. * MeV, ConstrainedIPMax=0.1 * mm, ConstrainedDIRAMin=9.0, FDMin=4.4 * mm, PVConstrainedMassMin=3500. * MeV, PVConstrainedMassMax=7000. * MeV, MinPVTracks=15, name='Velo2LongHlt2TrackEff_Bu2Jpsi2eeK_VELO_Filter_{hash}') TrackEffFilter = ParticleFilter(TrackEffFilterCode.Output, F.FILTER(F.MASS > 0)) return Hlt2Line( name=name, algs=upfront_reconstruction() + [require_pvs(pvs), tagandprobe_jpsi, TrackEffFilter], prescale=prescale, persistreco=True, #hlt1_filter_code="Hlt1.*Track.*MVADecision", )