##############################################################################
# (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",
)