diff --git a/CODEOWNERS b/CODEOWNERS index 60647c93915a7130ab2739de5be9b7ac00e40f77..6ca68d539388f176bbb46877cb2053918aa679e8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -137,6 +137,7 @@ /Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/rare_charm_*.py @dbrundu @dmitzel @smaccoli /Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/taggers.py @lpica @mstahl @tpajero /Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/d0_to_pi0pi0.py @smaccoli +/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/d_to_ksksh.py @gesarpis # Inclusive detached dilepton lines /Hlt/Hlt2Conf/python/Hlt2Conf/lines/inclusive_detached_dilepton/cutbased_dilepton_*.py @jagoodin /Hlt/Hlt2Conf/python/Hlt2Conf/lines/inclusive_detached_dilepton/dilepton_mva_builder.py @lecarus diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/__init__.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/__init__.py index 9edd2dbf9e3e37361314d335ba08a475c074e29f..3b523a1a989749fb554c1ddf43a3807244475668 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/__init__.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/__init__.py @@ -35,6 +35,7 @@ from . import ( d_to_hhh, d_to_hhhgamma, d_to_ksh, + d_to_ksksh, detection_asymmetry_lines, dsstar_to_dspipi, dst_to_dee, @@ -63,6 +64,7 @@ all_lines.update(d_to_etah.all_lines) all_lines.update(d_to_hhh.all_lines) all_lines.update(d_to_hhhgamma.all_lines) all_lines.update(d_to_ksh.all_lines) +all_lines.update(d_to_ksksh.all_lines) all_lines.update(d0_to_hh.all_lines) all_lines.update(d0_to_hhgamma.all_lines) all_lines.update(d0_to_hhhh.all_lines) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/d_to_ksksh.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/d_to_ksksh.py new file mode 100644 index 0000000000000000000000000000000000000000..90141a6ee297af979f0ab5f1fdfc68ffb0e5dacd --- /dev/null +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/d_to_ksksh.py @@ -0,0 +1,413 @@ +############################################################################### +# (c) Copyright 2021-2025 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. # +############################################################################### +"""Following lines are defined: + + 1. D+ -> KS0 (-> pi+ pi-) KS0 (-> pi+ pi-) pi+ + 2. D+ -> KS0 (-> pi+ pi-) KS0 (-> pi+ pi-) K+ + 3. D_s+ -> KS0 (-> pi+ pi-) KS0 (-> pi+ pi-) pi+ + 4. D_s+ -> KS0 (-> pi+ pi-) KS0 (-> pi+ pi-) K+ + +All LLLL, LLDD and DDDD modes of KS are included as a separate lines. +The D+ and Ds+ decaying to the same final state are included in same line as +in Run 2. + +TODO: +- check OWNPVIPCHI2 based on the data +- apply OWNPVLTIME requirements to KS and D(s)+ when functor will be fully working / not flooding logs with warnings +""" + +import Functors as F +import Functors.math as fmath +from Functors.math import in_range +from GaudiKernel.SystemOfUnits import GeV, MeV, mm +from GaudiKernel.SystemOfUnits import micrometer as um +from GaudiKernel.SystemOfUnits import picosecond as ps +from Moore.config import register_line_builder +from Moore.lines import Hlt2Line +from RecoConf.algorithms_thor import ParticleCombiner, ParticleFilter +from RecoConf.reconstruction_objects import make_pvs +from RecoConf.standard_particles import ( + make_has_rich_down_pions, + make_has_rich_long_kaons, + make_has_rich_long_pions, + make_KsTT, +) + +from .prefilters import charm_prefilters + +# from . import charm_isolation as isolation + +all_lines = {} + +################### +## track filters ## +################### + +## The code is based on a similar line: https://gitlab.cern.ch/lhcb/Moore/-/blob/master/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charm/d_to_ksh.py + + +def filter_long_pions( + pvs, pt_min=250 * MeV, p_min=2 * GeV, mipchi2_min=4.0, pion_pidk_max=5.0 +): + """Filter long pions with P PT, MINIPCHI2CUT and PIDk 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=250 * MeV, p_min=3 * GeV, mipchi2_min=4.0, pidk_min=-5.0 +): + """Filter long kaons with P PT, MINIPCHI2CUT and PIDk 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_pions_from_ks( + pvs, pt_min=250 * MeV, p_min=2 * GeV, mipchi2_min=35.0, pion_pidk_max=15.0 +): + """Filter long pions with P PT, MINIPCHI2CUT and PIDk 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_down_pions_from_ks(pvs, pt_min=250 * MeV, p_min=2 * GeV, pion_pidk_max=15.0): + """Filter downstream pions with P PT, and PIDk 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)) + + +####################### +## strange combiners ## +####################### +def make_ks_ll( + pions1, + pions2, + pvs, + name="Charm_DToKsKsH_KsLL_{hash}", + comb_m_min=445 * MeV, + comb_m_max=550 * MeV, + m_min=460 * MeV, + m_max=535 * MeV, + comb_pt_min=500 * MeV, + pt_min=300 * MeV, + comb_p_min=3.5 * GeV, + p_min=4 * GeV, + doca_max=1 * mm, + vchi2pdof_max=8.0, + ownpvvdz_min=10 * mm, + ownpvfdchi2_min=3.0, + mipchi2_min=9.0, + ownpvltime_min=1.0 * ps, +): + """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.OWNPVVDZ > ownpvvdz_min, + F.OWNPVFDCHI2 > ownpvfdchi2_min, + F.MINIPCHI2CUT(IPChi2Cut=mipchi2_min, Vertices=pvs), + F.OWNPVLTIME > ownpvltime_min, + ) + return ParticleCombiner( + [pions1, pions2], + name=name, + DecayDescriptor="KS0 -> pi+ pi-", + CombinationCut=comb_cut, + CompositeCut=vertex_cut, + ) + + +def make_ks_dd( + pions1, + pions2, + name="Charm_DToKsKsH_KsDD_{hash}", + comb_m_min=417 * MeV, + comb_m_max=577 * MeV, + m_min=437 * MeV, + m_max=557 * MeV, + comb_pt_min=500 * MeV, + pt_min=450 * MeV, + sum_pt_min=500 * MeV, + comb_p_min=4.5 * GeV, + p_min=5 * GeV, + doca_max=2 * mm, + docachi2_max=12.0, + vchi2pdof_max=8.0, + mipchi2_min=9.0, + ownpvltime_min=1.0 * ps, +): + """ + 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.OWNPVLTIME > ownpvltime_min, + ) + return ParticleCombiner( + [pions1, pions2], + name=name, + DecayDescriptor="KS0 -> pi+ pi-", + CombinationCut=comb_cut, + CompositeCut=vertex_cut, + ) + + +######################## +## D mesons combiners ## +######################## +def combine_d_ks_ks_h( + ks0, + ks1, + particle, + pvs, + decay_descriptor, + name="Charm_DToKsKsH_DToKsKsHCombiner_{hash}", + comb_m_min=1765 * MeV, + comb_m_max=2065 * MeV, + m_min=1840 * MeV, # lower range for D+ + m_max=2090 * MeV, # upper range for Ds+ + comb_pt_min=3 * GeV, + pt_min=2.0 * GeV, + sum_pt_min=1.5 * GeV, + comb_p_min=20 * GeV, + p_min=16 * GeV, + doca_max=100 * um, + vchi2pdof_max=8.0, + ownpvltime_min=0.4 * ps, + ownpvvdz_min=0.5 * mm, + ownpvfdchi2_min=25.0, + bpfdrho_max=5 * mm, + ownpvdira_min=0.9995, + dz1_min=20.0 * mm, + ks_fdrho_min=1 * mm, + dzero_vz_min=-300 * mm, +): + """Combine D meson with Ks Ks and additonal hadron (K/pi). + Make MASS, P, PT, SUM(PT), MAXDOCACUT cuts in the combination; + MASS, P, PT, CHI2DOF, OWNPVVDZ, OWNPVFDCHI2, OWNPVIPCHI2, OWNPVDIRA cuts after the vertex fit. + Cuts generally based on Run2 Turbo lines. + """ + comb_cut = F.require_all( + 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, + ) + + if doca_max is not None: + comb_cut &= F.MAXSDOCACUT(doca_max) + + vertex_cut = F.require_all( + in_range(m_min, F.MASS, m_max), + F.END_VZ > dzero_vz_min, + F.OWNPVVDRHO < bpfdrho_max, + F.PT > pt_min, + F.P > p_min, + F.CHI2DOF < vchi2pdof_max, + F.OWNPVLTIME > ownpvltime_min, + F.OWNPVVDZ > ownpvvdz_min, + F.OWNPVFDCHI2 > ownpvfdchi2_min, + F.OWNPVDIRA > ownpvdira_min, + F.CHILD(1, F.END_VZ) - F.END_VZ > dz1_min, + fmath.sqrt( + (F.CHILD(1, F.END_VX) - F.END_VX) ** 2 + + (F.CHILD(1, F.END_VY) - F.END_VY) ** 2 + ) + > ks_fdrho_min, + F.CHILD(2, F.END_VZ) - F.END_VZ > dz1_min, + fmath.sqrt( + (F.CHILD(2, F.END_VX) - F.END_VX) ** 2 + + (F.CHILD(2, F.END_VY) - F.END_VY) ** 2 + ) + > ks_fdrho_min, + ) + return ParticleCombiner( + [ks0, ks1, particle], + name=name, + AllowDiffInputsForSameIDChildren=True, + DecayDescriptor=decay_descriptor, + CombinationCut=comb_cut, + CompositeCut=vertex_cut, + ) + + +########################### +## Hlt2 lines definition ## +########################### +@register_line_builder(all_lines) +def dp_to_kskspi_llll_line(name="Hlt2Charm_DpDspToKsKsPip_LLLL", prescale=1): + pvs = make_pvs() + long_pions = filter_long_pions(pvs) + ks_ll = make_ks_ll( + filter_long_pions_from_ks(pvs), filter_long_pions_from_ks(pvs), pvs + ) + dp_kskspi_llll = combine_d_ks_ks_h( + ks_ll, + ks_ll, + long_pions, + pvs, + "[D+ -> KS0 KS0 pi+]cc", + name=f"{name}_Combiner", + ) + return Hlt2Line( + name=name, + algs=charm_prefilters() + [ks_ll, dp_kskspi_llll], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +def dp_to_kskspi_lldd_line(name="Hlt2Charm_DpDspToKsKsPip_LLDD", prescale=1): + pvs = make_pvs() + long_pions = filter_long_pions(pvs) + ks_ll = make_ks_ll( + filter_long_pions_from_ks(pvs), filter_long_pions_from_ks(pvs), pvs + ) + ks_dd = make_ks_dd(filter_down_pions_from_ks(pvs), filter_down_pions_from_ks(pvs)) + dp_kskspi_lldd = combine_d_ks_ks_h( + ks_ll, + ks_dd, + long_pions, + pvs, + "[D+ -> KS0 KS0 pi+]cc", + name=f"{name}_Combiner", + doca_max=2.0 * mm, + dz1_min=50.0 * mm, + ks_fdrho_min=10 * mm, + ) + return Hlt2Line( + name=name, + algs=charm_prefilters() + [ks_ll, ks_dd, dp_kskspi_lldd], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +def dp_to_kskspi_dddd_line(name="Hlt2Charm_DpDspToKsKsPip_DDDD", prescale=1): + pvs = make_pvs() + long_pions = filter_long_pions(pvs) + ks_dd = make_ks_dd(filter_down_pions_from_ks(pvs), filter_down_pions_from_ks(pvs)) + dp_kskspi_dddd = combine_d_ks_ks_h( + ks_dd, + ks_dd, + long_pions, + pvs, + "[D+ -> KS0 KS0 pi+]cc", + name=f"{name}_Combiner", + doca_max=2.0 * mm, + dz1_min=50.0 * mm, + ks_fdrho_min=10 * mm, + ) + return Hlt2Line( + name=name, algs=charm_prefilters() + [ks_dd, dp_kskspi_dddd], prescale=prescale + ) + + +@register_line_builder(all_lines) +def dp_to_kskskp_llll_line(name="Hlt2Charm_DpDspToKsKsKp_LLLL", prescale=1): + pvs = make_pvs() + long_kaons = filter_long_kaons(pvs) + ks_ll = make_ks_ll( + filter_long_pions_from_ks(pvs), filter_long_pions_from_ks(pvs), pvs + ) + dp_kskskp_llll = combine_d_ks_ks_h( + ks_ll, + ks_ll, + long_kaons, + pvs, + "[D+ -> KS0 KS0 K+]cc", + name=f"{name}_Combiner", + ) + return Hlt2Line( + name=name, algs=charm_prefilters() + [ks_ll, dp_kskskp_llll], prescale=prescale + ) + + +@register_line_builder(all_lines) +def dp_to_kskskp_lldd_line(name="Hlt2Charm_DpDspToKsKsKp_LLDD", prescale=1): + pvs = make_pvs() + long_kaons = filter_long_kaons(pvs) + ks_ll = make_ks_ll( + filter_long_pions_from_ks(pvs), filter_long_pions_from_ks(pvs), pvs + ) + ks_dd = make_ks_dd(filter_down_pions_from_ks(pvs), filter_down_pions_from_ks(pvs)) + dp_kskskp_lldd = combine_d_ks_ks_h( + ks_ll, + ks_dd, + long_kaons, + pvs, + "[D+ -> KS0 KS0 K+]cc", + name=f"{name}_Combiner", + doca_max=2.0 * mm, + dz1_min=50.0 * mm, + ks_fdrho_min=10 * mm, + ) + return Hlt2Line( + name=name, + algs=charm_prefilters() + [ks_ll, ks_dd, dp_kskskp_lldd], + prescale=prescale, + # extra_outputs=isolation.make_iso_particles(dp_kskp_ld, coneangle=0.5, DownstreamTrackIso=True) + ) + + +@register_line_builder(all_lines) +def dp_to_kskskp_dddd_line(name="Hlt2Charm_DpDspToKsKsKp_DDDD", prescale=1): + pvs = make_pvs() + long_kaons = filter_long_kaons(pvs) + ks_dd = make_ks_dd(filter_down_pions_from_ks(pvs), filter_down_pions_from_ks(pvs)) + dp_kskskp_dddd = combine_d_ks_ks_h( + ks_dd, + ks_dd, + long_kaons, + pvs, + "[D+ -> KS0 KS0 K+]cc", + name=f"{name}_Combiner", + doca_max=2.0 * mm, + dz1_min=50.0 * mm, + ks_fdrho_min=10 * mm, + ) + return Hlt2Line( + name=name, algs=charm_prefilters() + [ks_dd, dp_kskskp_dddd], prescale=prescale + )