diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leplep.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leplep.py index 7f7f5ecc4e7a9e95150fc5b0838336049b853e2b..322294c6ac5aab5aedc499ea63b4b795f8e549dd 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leplep.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leplep.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 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". # @@ -31,8 +31,11 @@ Hlt2QEE_BpToMajoE_MajoToMuE_SS_DD_Tight: tight PID cut line for the B(_c)+ -> e Hlt2QEE_BpToMajoE_MajoToMuE_OS_LL_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu- e, LL) Hlt2QEE_BpToMajoE_MajoToMuE_OS_DD_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mu- e, DD) -Contact: Louis Henry, louis.henry@cern.ch - +Contact: +- Spencer Collaviti, spencer.collaviti@cern.ch +- Andrea Merli, andrea.merli@cern.ch +- Lisa Fantini, lisa.fantini@cern.ch +- Viacheslav Duk, viacheslav.duk@cern.ch """ import Functors as F @@ -93,20 +96,20 @@ def _custom_monitoring_params(pvs, ptName): defaultCutDict = { "LL": { - "AllTracks": {"P": 2000.0 * MeV}, + "AllTracks": {"P": 2000.0 * MeV, "TRCHI2NDOFMAX": 3.0, "GHOSTPROBMAX": 0.4}, "Children": {"MINIPCHI2PV": 200}, "Bachelor": {}, "Child1": {}, "Child2": {}, "Majo": { "FDCHI2MIN": 2000, - "MASS": [0.2 * GeV, 6.0 * GeV], + "MASS": [1.0 * GeV, 6.5 * GeV], "MINIPCHI2PV": 20, }, "B": {"MASS": [4.0 * GeV, 7.0 * GeV]}, }, "DD": { - "AllTracks": {"P": 2000.0 * MeV}, + "AllTracks": {"P": 2000.0 * MeV, "TRCHI2NDOFMAX": 3.0, "GHOSTPROBMAX": 0.4}, "Children": {}, "Bachelor": {}, "Child1": {}, @@ -117,7 +120,7 @@ defaultCutDict = { } child_muon_pidCut = [F.ISMUON, F.PID_MU > 0.0] -bach_muon_pidCut = [F.PID_MU > 2] +bach_muon_pidCut = [F.ISMUON, F.PID_MU > 2] electron_pidCut = [F.PID_E > -2] @@ -136,13 +139,16 @@ def buildFromList(cutList): # Build a cut from a dictionnary -def build_cut_on_track(cutDict, pvs): +def build_cut_on_track(cutDict, pvs, corrm=False): listOfFunctors = [] for cut, cutVal in cutDict.items(): if cut == "PIDCuts": listOfFunctors += cutVal elif cut == "MASS": - listOfFunctors.append(in_range(cutVal[0], F.MASS, cutVal[1])) + if corrm: + listOfFunctors.append(in_range(cutVal[0], F.OWNPVCORRM(), cutVal[1])) + else: + listOfFunctors.append(in_range(cutVal[0], F.MASS, cutVal[1])) else: functors = { "P": F.P, @@ -151,6 +157,8 @@ def build_cut_on_track(cutDict, pvs): "FDCHI2MIN": F.OWNPVFDCHI2, "FDCHI2MAX": F.OWNPVFDCHI2, "MAXDOCA": F.MAXDOCA(), + "TRCHI2NDOFMAX": F.CHI2DOF, + "GHOSTPROBMAX": F.GHOSTPROB, } if "MAX" in cut: listOfFunctors.append(functors[cut] < cutVal) @@ -161,18 +169,10 @@ def build_cut_on_track(cutDict, pvs): return buildFromList(listOfFunctors) -def build_combination_cut(cutDict, pvs): - listOfFunctors = [] - for cut, cutVal in cutDict.items(): - if cut == "MASS": - listOfFunctors.append(in_range(cutVal[0], F.MASS, cutVal[1])) - # Build the cut - return buildFromList(listOfFunctors) - +def builder_BToMajoL_line(name, bachelorName, children, cutDict=None, prescale=1): + if cutDict is None: + cutDict = {} -def builder_BToMajoL_line( - name, bachelorName, children, cutDict={}, prescale=1, persistreco=True -): pvs = make_pvs # Interpret name into a B decay majoDecayDescr = "KS0 -> " + children[0] + "+ " + children[1] + "-" @@ -221,8 +221,7 @@ def builder_BToMajoL_line( [firstChild, secondChild], name="Majo2" + children[0] + children[1] + "_" + recoMode + "_{hash}", DecayDescriptor=majoDecayDescr, - CombinationCut=build_combination_cut(cutDict["Majo"], pvs), - CompositeCut=build_cut_on_track(cutDict["Majo"], pvs), + CompositeCut=build_cut_on_track(cutDict["Majo"], pvs, corrm=True), ) # Bachelor bachelor = make_filter_tracks( @@ -235,7 +234,6 @@ def builder_BToMajoL_line( [majo, bachelor], name=name + "_{hash}", DecayDescriptor="[B+ -> KS0 " + bachelorName + "+]cc", - CombinationCut=build_combination_cut(cutDict["B"], pvs), CompositeCut=build_cut_on_track(cutDict["B"], pvs), ) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leppi.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leppi.py index 251007ac133f7f78f60259eb21e9ee90e8e7e345..59a4e00a129da2cfa0c2aa1352d36b82b6892c4a 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leppi.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/b_to_majolep_majo_to_leppi.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 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". # @@ -21,6 +21,8 @@ List of lines (LL - Long, DD - downstream): Hlt2QEE_BpToMajoMu_MajoToEPi_LL_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> epi, LL) Hlt2QEE_BpToMajoE_MajoToMuPi_LL_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mupi, LL) +Hlt2QEE_BpToMajoMu_MajoToMuPi_LL_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> mupi, LL) +Hlt2QEE_BpToMajoE_MajoToEPi_LL_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> epi, LL) Hlt2QEE_BpToMajoMu_MajoToEPi_DD_Tight: tight PID cut line for the B(_c)+ -> mu HNL(-> epi, DD) Hlt2QEE_BpToMajoE_MajoToMuPi_DD_Tight: tight PID cut line for the B(_c)+ -> e HNL(-> mupi, DD) @@ -33,9 +35,11 @@ Hlt2QEE_BuToKs0Pi_Ks0ToPiPi_LL: B+ -> pi KS0 (->pipi) LL line Hlt2QEE_BuToKs0Pi_Ks0ToPiPi_DD: B+ -> pi KS0 (->pipi) DD line -Contact: Lera Lukashenko, valeriia.lukashenko@cern.ch -Contact: Louis Henry, louis.henry@cern.ch - +Contact: +- Spencer Collaviti, spencer.collaviti@cern.ch +- Andrea Merli, andrea.merli@cern.ch +- Lisa Fantini, lisa.fantini@cern.ch +- Viacheslav Duk, viacheslav.duk@cern.ch """ import Functors as F @@ -74,7 +78,7 @@ def BpToMajoMu_MajoToEPi_LL_Tight_line( muons = make_majorana_lepton( leptons=make_long_muons, - name="majo_long_muons_{hash}", + name="majo_B_long_muons_{hash}", pvs=pvs, pt_min=700 * MeV, pid=F.ISMUON, @@ -124,7 +128,7 @@ def BpToMajoE_MajoToMuPi_LL_Tight_line( pvs = make_pvs muons = make_majorana_lepton( - leptons=make_long_muons, name="majo_long_muons_{hash}", pvs=pvs, pid=F.ISMUON + leptons=make_long_muons, name="majo_N_long_muons_{hash}", pvs=pvs, pid=F.ISMUON ) electrons_with_brem = make_majorana_lepton( @@ -165,6 +169,105 @@ def BpToMajoE_MajoToMuPi_LL_Tight_line( ) +@register_line_builder(all_lines) +def BpToMajoMu_MajoToMuPi_LL_Tight_line( + name="Hlt2QEE_BpToMajoMu_MajoToMuPi_LL_Tight", prescale=1 +): + pvs = make_pvs + + b_muons = make_majorana_lepton( + leptons=make_long_muons, + name="majo_B_long_muons_{hash}", + pvs=pvs, + pt_min=700 * MeV, + pid=F.ISMUON, + ) + + muons = make_majorana_lepton( + leptons=make_long_muons, name="majo_N_long_muons_{hash}", pvs=pvs, pid=F.ISMUON + ) + + pions = make_majorana_lepton( + leptons=make_long_pions, + name="majo_long_pions_{hash}", + pvs=pvs, + pid=(F.PID_E <= 0.0), + ) + + majoranas2piMu = make_majorana( + name="Majo2mupi_{hash}", + child1=pions, + child2=muons, + descriptor="[KS0 -> mu- pi+]cc", + ) + + b2MuN = make_bhadron_majorana( + name="B2MajoMu_{hash}", + majoranas=majoranas2piMu, + bachelor=b_muons, + pvs=pvs, + descriptor="[B+ -> KS0 mu+]cc", + ) + + return Hlt2Line( + name=name, + algs=hnl_prefilter() + [majoranas2piMu, b2MuN], + prescale=prescale, + monitoring_variables=_HNL_MONITORING_VARIABLES, + ) + + +@register_line_builder(all_lines) +def BpToMajoE_MajoToEPi_LL_Tight_line( + name="Hlt2QEE_BpToMajoE_MajoToEPi_LL_Tight", prescale=1 +): + pvs = make_pvs + + b_electrons_with_brem = make_majorana_lepton( + leptons=make_long_electrons_with_brem, + name="majo_long_electrons_with_brem_{hash}", + pvs=pvs, + pt_min=700 * MeV, + pid=(F.PID_E > 1.0), + ) + + electrons_with_brem = make_majorana_lepton( + leptons=make_long_electrons_with_brem, + name="majo_long_electrons_with_brem_{hash}", + pvs=pvs, + pid=(F.PID_E > 1.0), + ) + + pions = make_majorana_lepton( + leptons=make_long_pions, + name="majo_long_pions_{hash}", + pvs=pvs, + pid=(F.PID_E <= 0.0), + ) + + majoranas2piE = make_majorana( + name="Majo2epi_{hash}", + child1=pions, + child2=electrons_with_brem, + descriptor="[KS0 -> e- pi+]cc", + ) + + b2EN = make_bhadron_majorana( + name="B2MajoE_{hash}", + majoranas=majoranas2piE, + bachelor=b_electrons_with_brem, + pvs=pvs, + descriptor="[B+ -> KS0 e+]cc", + ) + + return Hlt2Line( + name=name, + algs=hnl_prefilter() + [majoranas2piE, b2EN], + prescale=prescale, + monitoring_variables=_HNL_MONITORING_VARIABLES, + ) + + @register_line_builder(all_lines) def BpToMajoMu_MajoToEPi_DD_Tight_line( name="Hlt2QEE_BpToMajoMu_MajoToEPi_DD_Tight", prescale=1 @@ -173,7 +276,7 @@ def BpToMajoMu_MajoToEPi_DD_Tight_line( muons = make_majorana_lepton( leptons=make_long_muons, - name="majo_long_muons_{hash}", + name="majo_B_long_muons_{hash}", pvs=pvs, pt_min=700 * MeV, pid=F.ISMUON, @@ -282,7 +385,7 @@ def BpToMajoMu_MajoToEPi_LL_Loose_line( muons = make_majorana_lepton( leptons=make_long_muons, - name="majo_long_muons_{hash}", + name="majo_B_long_muons_{hash}", pvs=pvs, pt_min=700 * MeV, pid=F.ISMUON, @@ -332,7 +435,7 @@ def BpToMajoE_MajoToMuPi_LL_Loose_line( pvs = make_pvs muons = make_majorana_lepton( - leptons=make_long_muons, name="majo_long_muons_{hash}", pvs=pvs, pid=F.ISMUON + leptons=make_long_muons, name="majo_N_long_muons_{hash}", pvs=pvs, pid=F.ISMUON ) electrons_with_brem = make_majorana_lepton( @@ -381,7 +484,7 @@ def BpToMajoMu_MajoToEPi_DD_Loose_line( muons = make_majorana_lepton( leptons=make_long_muons, - name="majo_long_muons_{hash}", + name="majo_B_long_muons_{hash}", pvs=pvs, pt_min=700 * MeV, pid=F.ISMUON, diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/busca.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/busca.py index fff207359d244d86a4fb7aa5ac8589f41b1bb853..f604750a6874b788ec328751c6b8859d48f4c9c8 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/busca.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/busca.py @@ -29,17 +29,16 @@ turbo_lines = {} @configurable def make_DownstreamBuScaInclusive2H_DD( - particles, descriptors="KS0 -> pi+ pi-", name="make_DownstreamBuScaInclusive2H_DD_{hash}", - make_pvs=_make_pvs, minP=1400 * MeV, minPT=175 * MeV, minZ=500 * mm, maxChi2=25, maxOWNPVIP=100.0, ): - busca_tracks = particles + busca_tracks = make_down_pions() + # Very simple cuts to filter soft tracks busca_tracks_filter_code = F.require_all(F.P > minP, F.PT > minPT) filtered_busca_tracks = ParticleFilter( @@ -47,7 +46,7 @@ def make_DownstreamBuScaInclusive2H_DD( ) # Do vertexing vertex_code = F.require_all( - F.CHI2DOF < maxChi2, F.END_VZ > minZ, F.OWNPVVDRHO < maxOWNPVIP + F.CHI2DOF < maxChi2, F.END_VZ > minZ, F.OWNPVIP < maxOWNPVIP ) busca_svs = ParticleCombiner( Inputs=[filtered_busca_tracks, filtered_busca_tracks], @@ -70,12 +69,30 @@ def DownstreamBuScaInclusive2H_DD( name="Hlt2QEE_DownstreamBuScaInclusive2H_DD", prescale=1.0 ): # We actually doesn't require any PID cuts, so it's inclusive - pions = make_down_pions() + algs = ( + upfront_reconstruction() + + [require_pvs(_make_pvs())] + + [make_DownstreamBuScaInclusive2H_DD()] + ) + return Hlt2Line( + name=name, + algs=algs, + hlt1_filter_code=["Hlt1DownstreamBuSca.*Decision"], + prescale=prescale, + monitoring_variables=("pt", "eta", "m", "vchi2", "ipchi2", "n_candidates"), + ) + +@register_line_builder(turbo_lines) +@configurable +def DownstreamBuScaInclusive2H_DD_same_sign( + name="Hlt2QEE_DownstreamBuScaInclusive2H_DD_same_sign", prescale=1.0 +): + # We actually doesn't require any PID cuts, so it's inclusive algs = ( upfront_reconstruction() + [require_pvs(_make_pvs())] - + [make_DownstreamBuScaInclusive2H_DD(pions)] + + [make_DownstreamBuScaInclusive2H_DD(descriptors="[KS0 -> pi+ pi+]cc")] ) return Hlt2Line( name=name, diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/dimuon_no_ip.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/dimuon_no_ip.py index 1d24b5fbdef97500252ff51300c40c1e19836efd..00ca38dca57af17c9e70de3282bc9b3912230006 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/dimuon_no_ip.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/dimuon_no_ip.py @@ -15,7 +15,7 @@ Define HLT2 line for ``DiMuonNoIP``. import Functors as F from Functors import require_all from Functors.math import in_range -from GaudiKernel.SystemOfUnits import GeV, MeV, mm +from GaudiKernel.SystemOfUnits import GeV, mm from Moore.config import register_line_builder from Moore.lines import Hlt2Line from PyConf import configurable @@ -38,8 +38,11 @@ def filter_muons(particles, pvs, min_ipchi2, pid_mu, p_min=5 * GeV): return ParticleFilter(particles, F.FILTER(cut)) -hlt1_dimuonnoip_filter = ["Hlt1DiMuonNoIPDecision"] -hlt1_dimuonnoipss_filter = ["Hlt1DiMuonNoIP_SSDecision"] +hlt1_dimuonnoip_filter = ["Hlt1DiMuonNoIPDecision", "Hlt1DiMuonNoIPNormDecision"] +hlt1_dimuonnoipss_filter = [ + "Hlt1DiMuonNoIP_SSDecision", + "Hlt1DiMuonNoIPNorm_SSDecision", +] @configurable @@ -78,69 +81,9 @@ def make_dimuons( @register_line_builder(turbo_lines) -def dimuonnoip_massrange1_line(name="Hlt2QEE_DiMuonNoIP_massRange1", prescale=1): +def dimuonnoip_line(name="Hlt2QEE_DiMuonNoIP", prescale=1): pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, max_mass=740 * MeV) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoip_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange2_line(name="Hlt2QEE_DiMuonNoIP_massRange2", prescale=1): - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, min_mass=740 * MeV, max_mass=1.1 * GeV) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoip_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange3_line(name="Hlt2QEE_DiMuonNoIP_massRange3", prescale=1): - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, min_mass=1.1 * GeV, max_mass=3 * GeV) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoip_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange4_line(name="Hlt2QEE_DiMuonNoIP_massRange4", prescale=1): - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, min_mass=3 * GeV, max_mass=3.2 * GeV) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoip_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange5_line(name="Hlt2QEE_DiMuonNoIP_massRange5", prescale=1): - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, min_mass=3.2 * GeV, max_mass=9 * GeV) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoip_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange6_line(name="Hlt2QEE_DiMuonNoIP_massRange6", prescale=1): - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, min_mass=9 * GeV) + dimuonnoip = make_dimuons(pvs) return Hlt2Line( name=name, algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], @@ -161,103 +104,9 @@ def displaceddimuon_line(name="Hlt2QEE_DisplacedDiMuon", prescale=1): @register_line_builder(turbo_lines) -def dimuonnoip_allmasses_line( - name="Hlt2QEE_DiMuonNoIP_prescaledAllMass", prescale=0.1 -): # prescale down to an acceptable rate - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoip_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange1_ss_line( - name="Hlt2QEE_DiMuonNoIP_massRange1_ss", prescale=0.1 -): - pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, max_mass=740 * MeV, opposite_sign=False) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoipss_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange2_ss_line( - name="Hlt2QEE_DiMuonNoIP_massRange2_ss", prescale=0.1 -): - pvs = make_pvs() - dimuonnoip = make_dimuons( - pvs, min_mass=740 * MeV, max_mass=1.1 * GeV, opposite_sign=False - ) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoipss_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange3_ss_line( - name="Hlt2QEE_DiMuonNoIP_massRange3_ss", prescale=0.1 -): - pvs = make_pvs() - dimuonnoip = make_dimuons( - pvs, min_mass=1.1 * GeV, max_mass=3 * GeV, opposite_sign=False - ) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoipss_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange4_ss_line( - name="Hlt2QEE_DiMuonNoIP_massRange4_ss", prescale=0.1 -): - pvs = make_pvs() - dimuonnoip = make_dimuons( - pvs, min_mass=3 * GeV, max_mass=3.2 * GeV, opposite_sign=False - ) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoipss_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange5_ss_line( - name="Hlt2QEE_DiMuonNoIP_massRange5_ss", prescale=0.1 -): - pvs = make_pvs() - dimuonnoip = make_dimuons( - pvs, min_mass=3.2 * GeV, max_mass=9 * GeV, opposite_sign=False - ) - return Hlt2Line( - name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], - prescale=prescale, - hlt1_filter_code=hlt1_dimuonnoipss_filter, - ) - - -@register_line_builder(turbo_lines) -def dimuonnoip_massrange6_ss_line( - name="Hlt2QEE_DiMuonNoIP_massRange6_ss", prescale=0.1 -): +def dimuonnoip_massrange1_ss_line(name="Hlt2QEE_DiMuonNoIP_ss", prescale=0.1): pvs = make_pvs() - dimuonnoip = make_dimuons(pvs, min_mass=9 * GeV, opposite_sign=False) + dimuonnoip = make_dimuons(pvs, opposite_sign=False) return Hlt2Line( name=name, algs=upfront_reconstruction() + [require_pvs(pvs), dimuonnoip], diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/diphoton.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/diphoton.py index 78da17aad8892365c1e03a11776318bcb3ebe827..f42246661114802d766e07832c949a2f539ed608 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/diphoton.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/diphoton.py @@ -15,7 +15,6 @@ Definition of ALPs->GammaGamma lines including B2GammaGamma author: Titus Mombächer date: 23.11.2021 -10.12.2021: at the moment misses photon PID. This is reported here https://gitlab.cern.ch/lhcb/Rec/-/issues/240 and this line needs review once Photon PID is available. """ import Functors as F @@ -27,27 +26,40 @@ from RecoConf.algorithms_thor import ParticleCombiner, ParticleFilter from RecoConf.reconstruction_objects import upfront_reconstruction from RecoConf.standard_particles import make_photons +from Hlt2Conf.lines.qee.qee_builders import parent_isolation_output + all_lines = {} @configurable def filter_qee_photons( - cl_min, et_min, e_min, name="filter_qee_photons_{hash}", make_particles=make_photons + cl_min, + isph_min, + et_min, + e_min, + name="filter_qee_photons_{hash}", + make_particles=make_photons, ): - """Photon filter, currently missing CL functor""" + """Photon filter""" - code = F.require_all(F.PT > et_min, F.P > e_min) + code = F.require_all( + F.PT > et_min, + F.P > e_min, + F.IS_NOT_H > cl_min, + F.IS_PHOTON > isph_min, + ) return ParticleFilter(make_particles(), F.FILTER(code), name=name) @configurable def make_diphoton( - min_cl_gamma=0.3, # CL is not implemented currently - min_pt_gamma=5 * GeV, - min_p_gamma=16.0 * GeV, - min_sumpt=12.0 * GeV, - min_B_pt=7.0 * GeV, - min_B_mass=2.0 * GeV, + min_cl_gamma=0.5, + min_isph_gamma=0.5, + min_pt_gamma=3.75 * GeV, + min_p_gamma=12.0 * GeV, + min_sumpt=8.0 * GeV, + min_B_pt=5.0 * GeV, + min_B_mass=3.5 * GeV, min_pt_asym=0.1, ): """ @@ -56,9 +68,11 @@ def make_diphoton( """ descriptor = "B_s0 -> gamma gamma" photons = filter_qee_photons( - cl_min=min_cl_gamma, et_min=min_pt_gamma, e_min=min_p_gamma - ) # CL is not implemented at the moment - + cl_min=min_cl_gamma, + isph_min=min_isph_gamma, + et_min=min_pt_gamma, + e_min=min_p_gamma, + ) sum_pt = F.CHILD(1, F.PT) + F.CHILD(2, F.PT) pt_asym = (F.CHILD(1, F.PT) - F.CHILD(2, F.PT)) / ( F.CHILD(1, F.PT) + F.CHILD(2, F.PT) @@ -93,5 +107,6 @@ def Hlt2_ALPsToGammaGamma( calo_clusters=True, calo_digits=True, prescale=prescale, + extra_outputs=parent_isolation_output("B_s0", diphotons), monitoring_variables=("m", "pt", "eta", "n_candidates"), ) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/drellyan.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/drellyan.py index dff1de5bdf78ce0030007d8bcae6c3c4ef96a0bf..696bd6670b113627f29d1d578d1082b000eeb37c 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/drellyan.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/drellyan.py @@ -161,11 +161,11 @@ for is_ss in [True, False]: ): pvs = make_pvs() + muon_min_p = 6 * GeV + if use_soft_muons: - muon_min_p = 10.0 * GeV muon_min_pt = 1.0 * GeV else: - muon_min_p = 17.5 * GeV muon_min_pt = 1.3 * GeV dimuons = make_dimuon_candidates( diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/high_mass_dimuon.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/high_mass_dimuon.py index d93ccf6d90dd53cf93f6e86a3b22d8f9658eb443..23d468d24fd066df2721f9b559276621a7eb3bf6 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/high_mass_dimuon.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/high_mass_dimuon.py @@ -48,6 +48,19 @@ def make_Z_cand(): return make_dimuon_novxt(muons_for_Z, muons_for_Z, "Z0 -> mu+ mu-") +@configurable +def make_A_cand(): + muons_for_A = muon_filter_for_Z(make_ismuon_long_muon(), min_pt=2.5 * GeV) + return make_dimuon_novxt( + muons_for_A, + muons_for_A, + "Z0 -> mu+ mu-", + min_combination_mass=12.0 * GeV, + min_composite_mass=15.0 * GeV, + max_combination_mass=60.0 * GeV, + ) + + @configurable def make_Z_cand_SingleNoMuID(): muon_ID, muon_noID = make_ismuon_long_muon(), make_long_muons() @@ -74,6 +87,19 @@ def make_Zss_cand(): ) +@configurable +def make_Ass_cand(): + muons = muon_filter_for_Z(make_ismuon_long_muon(), min_pt=2.5 * GeV) + return make_dimuon_novxt( + muons, + muons, + "[Z0 -> mu- mu-]cc", + min_combination_mass=12.0 * GeV, + min_composite_mass=15.0 * GeV, + max_combination_mass=60.0 * GeV, + ) + + @configurable def make_dimuon_novxt( input_muon1, @@ -81,11 +107,18 @@ def make_dimuon_novxt( decaydescriptor, min_combination_mass=z_min_combination_mass, min_composite_mass=z_min_composite_mass, + max_combination_mass=None, ): """Dimuon combination with only a mass cut""" # pre-vertex fit, useful for controlling the combinatorics - combination_code = F.MASS > min_combination_mass + if max_combination_mass is not None: + combination_code = F.require_all( + F.MASS > min_combination_mass, F.MASS < max_combination_mass + ) + else: + combination_code = F.MASS > min_combination_mass + # post-vertex fit composite_code = F.MASS > min_composite_mass @@ -133,6 +166,20 @@ def z_to_mu_mu_line(name="Hlt2QEE_ZToMuMuFull", prescale=1, persistreco=True): ) +@register_line_builder(all_lines) +@configurable +def A_to_mu_mu_line(name="Hlt2QEE_AToMuMuFull", prescale=1, persistreco=True): + """A decay to two muons line, both requiring ismuon""" + A2mumu = make_A_cand() + return Hlt2Line( + name=name, + algs=upfront_reconstruction() + [A2mumu], + prescale=prescale, + persistreco=persistreco, + monitoring_variables=_default_monitoring_variables, + ) + + @register_line_builder(all_lines) @configurable def z_to_mu_mu_single_nomuid_line( @@ -192,3 +239,19 @@ def same_sign_dimuon_line( persistreco=persistreco, monitoring_variables=_default_monitoring_variables, ) + + +@register_line_builder(all_lines) +@configurable +def A_to_mu_mu_same_sign_line( + name="Hlt2QEE_AToMuMuSameSignFull", prescale=1, persistreco=True +): + """A decay to two same-sign muons line""" + A2mumuss = make_Ass_cand() + return Hlt2Line( + name=name, + algs=upfront_reconstruction() + [A2mumuss], + prescale=prescale, + persistreco=persistreco, + monitoring_variables=_default_monitoring_variables, + ) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/jets.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/jets.py index d63b238c2d97dd0389e2913866b09dedb6bc1664..22f571f31ca9080ddad86145184d9afed39d4ec0 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/jets.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/jets.py @@ -22,6 +22,7 @@ all_lines = {} all_reference_run_lines = {} _hlt1_light_jet_filter = ["Hlt1ConeJet(15|30|50|100)GeVDecision"] +_hlt1_gamma_ee_filter = ["Hlt1DownstreamGammaToEEDecision"] _default_monitoring_variables = ("pt", "eta", "n_candidates", "ipchi2", "vchi2") @@ -262,12 +263,7 @@ def IncDiJet35GeV_line(name="Hlt2QEE_IncDiJet35GeVFull", prescale=1.0): ) -# Gamma + jet lines. -# TODO: For now, only add these for the pp reference run. -all_reference_run_lines.update(all_lines) - - -@register_line_builder(all_reference_run_lines) +@register_line_builder(all_lines) @configurable def GammaLLJet_line( name="Hlt2QEE_GammaLLJetFull", prescale=1, persistreco=True, hlt1_filter=True @@ -284,7 +280,7 @@ def GammaLLJet_line( ) -@register_line_builder(all_reference_run_lines) +@register_line_builder(all_lines) @configurable def GammaDDJet_line( name="Hlt2QEE_GammaDDJetFull", prescale=1, persistreco=True, hlt1_filter=True @@ -293,9 +289,15 @@ def GammaDDJet_line( return Hlt2Line( name=name, algs=upfront_reconstruction() + [require_pvs(make_pvs()), combos], - hlt1_filter_code=_hlt1_light_jet_filter if hlt1_filter else "", + hlt1_filter_code=_hlt1_light_jet_filter + _hlt1_gamma_ee_filter + if hlt1_filter + else "", prescale=prescale, calo_clusters=True, persistreco=persistreco, monitoring_variables=_default_monitoring_variables, ) + + +# Gamma + jet lines. +all_reference_run_lines.update(all_lines) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/qee_builders.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/qee_builders.py index 42d3a0be9411df4862ab040e08936292308276ed..84919051c8fc36d440fd53e973a73bfc6fc902e6 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/qee_builders.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/qee_builders.py @@ -1,5 +1,5 @@ ############################################################################### -# Copyright 2019-2023 CERN for the benefit of the LHCb Collaboration # +# Copyright 2019-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". # @@ -14,26 +14,32 @@ Definition of useful QEE filters and builders import Functors as F from Functors.math import in_range -from GaudiKernel.SystemOfUnits import GeV, MeV, degree, meter +from GaudiKernel.SystemOfUnits import GeV, MeV, degree, meter, micrometer from PyConf import configurable -from RecoConf import rdbuilder_thor from RecoConf.algorithms_thor import ParticleCombiner, ParticleFilter from RecoConf.event_filters import filter_max_pvs -from RecoConf.rdbuilder_thor import make_rd_detached_kaons from RecoConf.reconstruction_objects import make_pvs from RecoConf.standard_particles import ( make_down_electrons_no_brem, + make_down_pions, + make_has_rich_long_kaons, make_has_rich_long_pions, make_ismuon_long_muon, make_long_electrons_with_brem, + make_long_kaons, make_long_muons, + make_long_pions, + make_merged_pi0s, make_mva_ttrack_kaons, make_mva_ttrack_muons, make_mva_ttrack_pions, make_photons, + make_ttrack_pions, + make_up_pions, ) from RecoConf.ttrack_selections_reco import PVF_with_single_extrapolation +from Hlt2Conf.isolation import extra_outputs_for_isolation from Hlt2Conf.lines.qee.high_mass_dielec import make_dielec_novxt from Hlt2Conf.lines.qee.high_mass_dimuon import make_dimuon_novxt from Hlt2Conf.standard_jets import make_jets @@ -46,8 +52,6 @@ def make_filter_tracks( name="qee_has_rich_long_pions", pt_min=0.25 * GeV, p_min=2.0 * GeV, - trchi2dof_max=3, # TBC with Reco - trghostprob_max=0.4, # TBC with Reco mipchi2dvprimary_min=None, pid=None, additionalCuts=None, @@ -71,6 +75,165 @@ def make_filter_tracks( return ParticleFilter(make_particles(), name=name, Cut=F.FILTER(code)) +@configurable +def make_qee_has_rich_detached_pions( + name="qee_has_rich_detached_pions_{hash}", + p_min=2.0 * GeV, + pt_min=250.0 * MeV, + mipchi2dvprimary_min=4.0, + pid=(F.PID_K <= 0.0), +): + """ + Return QEE detached pions with hasRich. + """ + return make_filter_tracks( + name=name, + make_particles=make_has_rich_long_pions, + p_min=p_min, + pt_min=pt_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid, + ) + + +@configurable +def make_qee_has_rich_detached_kaons( + name="qee_has_rich_detached_kaons_{hash}", + p_min=2.0 * GeV, + pt_min=250.0 * MeV, + mipchi2dvprimary_min=4.0, + pid=(F.PID_K > 0.0), +): + """ + Return QEE detached kaons with hasRich. + """ + return make_filter_tracks( + name=name, + make_particles=make_has_rich_long_kaons, + p_min=p_min, + pt_min=pt_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid, + ) + + +@configurable +def make_qee_detached_muons( + name="qee_detached_muons_{hash}", + pt_min=250.0 * MeV, + p_min=3000.0 * MeV, + mipchi2dvprimary_min=3.0, + pid=F.require_all(F.ISMUON, F.PID_MU > 0.0), +): + """ + Return QEE detached muons. + """ + return make_filter_tracks( + name=name, + make_particles=make_long_muons, + pt_min=pt_min, + p_min=p_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid, + ) + + +@configurable +def make_qee_detached_kaons( + mipchi2dvprimary_min, + name="qee_detached_kaons_{hash}", + p_min=2.0 * GeV, + pt_min=250.0 * MeV, + pid=(F.PID_K > -2.0), +): + """ + Return QEE detached kaons. + """ + return make_filter_tracks( + name=name, + make_particles=make_long_kaons, + p_min=p_min, + pt_min=pt_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid, + ) + + +@configurable +def make_qee_detached_pions( + p_min, + pt_min, + name="qee_detached_pions_{hash}", + mipchi2dvprimary_min=25.0, + pid=(F.PID_K <= 1e-5), +): + """ + Return QEE detached pions. + """ + return make_filter_tracks( + name=name, + make_particles=make_long_pions, + p_min=p_min, + pt_min=pt_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid, + ) + + +@configurable +def make_qee_detached_kstar0s( + name="qee_detached_kstar0s_{hash}", + am_min=0 * MeV, + am_max=2600.0 * MeV, + track_p_min=2.0 * GeV, + track_pt_min=250.0 * MeV, + track_ipchi2_min=9.0, + # if the RICH can not distinguish between kaons and pions then + # the PID will be set to zero, and most often these will correspond + # to real pions + pi_pid=F.PID_K < 1e-5, + k_pid=(F.PID_K > 2.0), + kstar0_pt_min=400.0 * MeV, + adocachi2cut=30.0, + maxdoca=500 * micrometer, + vchi2pdof_max=16.0, +): + """ + Build Kstar0 candidates. Approximately corresponding to the Run2 + "StdVeryLooseDetachedKstar" cuts. + + Copied from RD implementation + """ + + pions = make_qee_has_rich_detached_pions( + p_min=track_p_min, + pt_min=track_pt_min, + mipchi2dvprimary_min=track_ipchi2_min, + pid=pi_pid, + ) + kaons = make_qee_has_rich_detached_kaons( + p_min=track_p_min, + pt_min=track_pt_min, + mipchi2dvprimary_min=track_ipchi2_min, + pid=k_pid, + ) + descriptor = "[K*(892)0 -> K+ pi-]cc" + combination_code = F.require_all( + in_range(am_min, F.MASS, am_max), + F.MAXSDOCACHI2CUT(adocachi2cut), + F.MAXSDOCACUT(maxdoca), + ) + vertex_code = F.require_all(F.CHI2DOF < vchi2pdof_max, F.PT > kstar0_pt_min) + + return ParticleCombiner( + [kaons, pions], + name=name, + DecayDescriptor=descriptor, + CombinationCut=combination_code, + CompositeCut=vertex_code, + ) + + @configurable def muon_filter(min_pt=0.0 * GeV, require_muID=True): # A muon filter: PT @@ -119,7 +282,7 @@ def make_qee_gamma_DD( ): electrons = elec_filter_down(min_pt=min_elec_pt, min_electron_id=min_elec_id) combination_code = F.MASS < am_max - vertex_code = F.require_all(F.PT > pt_min, F.MASS < am_max, F.CHI2 < maxVertexChi2) + vertex_code = F.require_all(F.PT > pt_min, F.MASS < m_max, F.CHI2 < maxVertexChi2) return ParticleCombiner( name=name, @@ -143,7 +306,7 @@ def make_qee_gamma_LL( ): electrons = elec_filter(min_pt=min_elec_pt, min_electron_id=min_elec_id) combination_code = F.MASS < am_max - vertex_code = F.require_all(F.PT > pt_min, F.MASS < am_max, F.CHI2 < maxVertexChi2) + vertex_code = F.require_all(F.PT > pt_min, F.MASS < m_max, F.CHI2 < maxVertexChi2) return ParticleCombiner( name=name, @@ -290,8 +453,8 @@ def make_majorana( child2, name="Generic_Majorana", descriptor="", - am_min=0.2 * GeV, - am_max=7.0 * GeV, + am_min=1.0 * GeV, + am_max=6.5 * GeV, adocachi2=16.0, pt_min=0.7 * GeV, vtxchi2_max=9.0, @@ -462,7 +625,7 @@ def make_BToLHNL_TT( ) -### basic particle filters for Dark Higgs searches +### basic particle filters for Dark Higgs and Heavy Neutral Lepton searches ## T tracks @configurable def filter_ttrack_muons_for_high_mass( @@ -479,6 +642,7 @@ def filter_ttrack_muons_for_high_mass( pid_k_max=10.0, pid_p_max=12.0, minipchi2_min=25.0, + pid_mu_diff_max=None, ): muons = make_muons() filter_code_high_mass = F.require_all( @@ -495,6 +659,11 @@ def filter_ttrack_muons_for_high_mass( F.ISMUON, F.MINIPCHI2(pvs) > minipchi2_min, ) + if pid_mu_diff_max is not None: + filter_code_high_mass &= F.require_all( + (F.PID_K - F.PID_MU) < pid_mu_diff_max, + (F.PID_P - F.PID_MU) < pid_mu_diff_max, + ) return ParticleFilter(muons, F.FILTER(filter_code_high_mass)) @@ -543,6 +712,7 @@ def filter_ttrack_pions_for_high_mass( track_min=10, minipchi2_min=25.0, chi2_max=10.0, + not_ismuon=False, ): pions = make_pions() filter_code_high_mass = F.require_all( @@ -556,6 +726,8 @@ def filter_ttrack_pions_for_high_mass( F.CHI2 < chi2_max, F.MINIPCHI2(pvs) > minipchi2_min, ) + if not_ismuon: + filter_code_high_mass &= F.require_all(~F.ISMUON) return ParticleFilter(pions, F.FILTER(filter_code_high_mass)) @@ -564,13 +736,12 @@ def filter_ttrack_pions_for_high_mass( def qee_ttrack_combiner_highmass( filtered_particle1, filtered_particle2, - pvs, decay_descriptor, yz_intersection_z_min=1500.0, yz_intersection_z_max=8000.0, maxdoca=400.0, maxdocachi2=20000.0, - vertex_z_max=8 * meter, + vertex_z_max=8.0 * meter, ownpv_dira_min=0.9996, max_chi2=20.0, ownpv_ip_max=150.0, @@ -578,6 +749,8 @@ def qee_ttrack_combiner_highmass( ownpv_vdrho_min=80.0, mass_min=2000.0, mass_max=100000.0, + pt_min=None, + use_corr_mass=False, ): """ used for T track combinations with a mass range > 2 GeV @@ -597,8 +770,13 @@ def qee_ttrack_combiner_highmass( F.OWNPVIP < ownpv_ip_max, F.OWNPVIPCHI2 < ownpv_ip_chi2_max, F.OWNPVVDRHO > ownpv_vdrho_min, - F.math.in_range(mass_min, F.MASS, mass_max), - ) # TODO: upper limit? + ) + if not use_corr_mass: + vertex_cut &= F.require_all(F.math.in_range(mass_min, F.MASS, mass_max)) + else: + vertex_cut &= F.require_all(F.math.in_range(mass_min, F.OWNPVCORRM(), mass_max)) + if pt_min is not None: + vertex_cut &= F.require_all(F.PT > pt_min) return ParticleCombiner( Inputs=[filtered_particle1, filtered_particle2], @@ -615,7 +793,7 @@ def qee_ttrack_combiner_highmass( def qee_filtered_long_kaons( ghostprob_max=0.4, pt_min=300 * MeV, p_min=3500.0 * MeV, chi2_max=60.0 ): - long_kaons = make_rd_detached_kaons() + long_kaons = make_qee_detached_kaons(mipchi2dvprimary_min=4.0) filter_multiple_kaons = F.require_all( F.GHOSTPROB < ghostprob_max, F.PT > pt_min, F.P > p_min, F.CHI2 < chi2_max ) @@ -628,8 +806,7 @@ def make_XtoTT_muons(pvs, mass_min=2000.0): return qee_ttrack_combiner_highmass( filtered_TT_muons, filtered_TT_muons, - pvs, - "[KS0 -> mu+ mu-]cc", + "KS0 -> mu+ mu-", mass_min=mass_min, ) @@ -638,7 +815,7 @@ def make_XtoTT_muons(pvs, mass_min=2000.0): def make_XtoTT_kaons(pvs): filtered_TT_kaons = filter_ttrack_kaons_for_high_mass(pvs) return qee_ttrack_combiner_highmass( - filtered_TT_kaons, filtered_TT_kaons, pvs, "[KS0 -> K+ K-]cc" + filtered_TT_kaons, filtered_TT_kaons, "KS0 -> K+ K-" ) @@ -649,7 +826,6 @@ def make_XtoTT_pimu(pvs): return qee_ttrack_combiner_highmass( filtered_TT_pions, filtered_TT_muons, - pvs, "[KS0 -> pi+ mu-]cc", ) @@ -664,7 +840,6 @@ def qee_BtoLX_TT( long_particles, ttrack_particles, decay_descriptor, - pvs, vertex_z_max=1000.0, mass_min=2200.0, mass_max=8300.0, @@ -758,7 +933,7 @@ def make_qee_detached_dihadron( if same_sign: descriptor = f"[{parent_id} -> pi+ pi+]cc" pvs = make_pvs() - pions = rdbuilder_thor.make_rd_detached_pions( + pions = make_qee_detached_pions( mipchi2dvprimary_min=min_ipchi2_track, pt_min=min_pt_track, p_min=min_p_track, @@ -780,3 +955,95 @@ def make_qee_detached_dihadron( CombinationCut=combination_code, CompositeCut=vertex_code, ) + + +def parent_isolation_output(name, candidate): + """ + Add default isolation for parent particle + Args: + name: String with prefix of the TES location for extra selection + candidate: Containers of reference particles to relate extra particles + """ + + return select_parts_for_isolation( + names=[name], candidates=[candidate], cut=((F.DR2 < 0.25) & ~F.FIND_IN_TREE()) + ) + + +@configurable +def select_parts_for_isolation( + names=[], + candidates=[], + cut=(F.DR2 < 0.25), + LongTrackIso=True, + TTrackIso=False, + DownstreamTrackIso=False, + UpstreamTrackIso=False, + NeutralIso=True, + PiZerosIso=False, +): + """ + Add to the extra_outputs different kind of isolations by properly setting the given flag. + Returns selection of extra particles for isolation. + + Args: + names: List of strings with prefix of the TES location for extra selection + candidates: List of containers of reference particles to relate extra particles + cut: Predicate to select extra information to persist. By default: cone geometry with max dr2=1 + LongTrackIso: Boolean flag to make isolation with long tracks. By default True. + TTrackIso: Boolean flag to make isolation with tt tracks. By default False. + DownstreamTrackIso: Boolean flag to make isolation with downstream tracks. By default False. + UpstreamTrackIso: Boolean flag to make isolation with upstream tracks. By default False. + NeutralIso: Boolean flag to make isolation with neutral particles. By default True. + PiZerosIso: Boolean flag to make isolation with neutral pions. By default False. + + """ + extra_outputs = [] + assert len(names) == len(candidates), ( + "Different number of names and candidate containers for particle isolation!" + ) + + for name, cand in zip(names, candidates): + if LongTrackIso: + extra_outputs += extra_outputs_for_isolation( + name=name + "_LongTrackIsolation", + extra_particles=make_long_pions(), + ref_particles=cand, + selection=cut, + ) + if TTrackIso: + extra_outputs += extra_outputs_for_isolation( + name=name + "_TTrackIsolation", + extra_particles=make_ttrack_pions(), + ref_particles=cand, + selection=cut, + ) + if DownstreamTrackIso: + extra_outputs += extra_outputs_for_isolation( + name=name + "_DownstreamTrackIsolation", + extra_particles=make_down_pions(), + ref_particles=cand, + selection=cut, + ) + if UpstreamTrackIso: + extra_outputs += extra_outputs_for_isolation( + name=name + "_UpstreamTrackIsolation", + extra_particles=make_up_pions(), + ref_particles=cand, + selection=cut, + ) + if NeutralIso: + extra_outputs += extra_outputs_for_isolation( + name=name + "_NeutralIsolation", + extra_particles=make_photons(), + ref_particles=cand, + selection=cut, + ) + if PiZerosIso: + extra_outputs += extra_outputs_for_isolation( + name=name + "_PiZerosIsolation", + extra_particles=make_merged_pi0s(), + ref_particles=cand, + selection=cut, + ) + return extra_outputs diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/quarkonia.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/quarkonia.py index d98e7248b13847f063fab2a020fec08b347f8ad3..48fb0e2a6a693fa44af84233c856a8aa6dc6bd47 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/quarkonia.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/quarkonia.py @@ -53,7 +53,6 @@ def make_muons( def make_dimuon( - pvs, name="qee_quarkonia_dimuon_{hash}", DecayDescriptor="J/psi(1S) -> mu+ mu-", min_pt_muon=0.0 * GeV, @@ -91,9 +90,8 @@ def make_dimuon( @configurable -def make_jpsi(pvs, min_ownpvdls=None, max_ownpvdls=None): +def make_jpsi(min_ownpvdls=None, max_ownpvdls=None): return make_dimuon( - pvs=pvs, name="qee_jpsi_{hash}", DecayDescriptor="J/psi(1S) -> mu+ mu-", min_pt_muon=MIN_PT_MUON, @@ -109,9 +107,8 @@ def make_jpsi(pvs, min_ownpvdls=None, max_ownpvdls=None): @configurable -def make_upsilon(pvs): +def make_upsilon(): return make_dimuon( - pvs=pvs, name="qee_upsilon_{hash}", DecayDescriptor="Upsilon(1S) -> mu+ mu-", min_pt_muon=MIN_PT_MUON, @@ -132,11 +129,10 @@ def jpsi_to_mu_mu_detached_line( name="Hlt2QEE_JpsiToMuMu_Detached", prescale=1, persistreco=False ): """Jpsi (Detached) decay to two muons line""" - pvs = make_pvs() - jpsi = make_jpsi(pvs=pvs, min_ownpvdls=3.0) + jpsi = make_jpsi(min_ownpvdls=3.0) return Hlt2Line( name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), jpsi], + algs=upfront_reconstruction() + [require_pvs(make_pvs()), jpsi], prescale=prescale, persistreco=persistreco, monitoring_variables=("m", "pt", "eta", "n_candidates"), @@ -149,11 +145,10 @@ def jpsi_to_mu_mu_prompt_line( name="Hlt2QEE_JpsiToMuMu_Prompt", prescale=0.1, persistreco=False ): """Jpsi (Prompt) decay to two muons line""" - pvs = make_pvs() - jpsi = make_jpsi(pvs=pvs, max_ownpvdls=3.0) + jpsi = make_jpsi(max_ownpvdls=3.0) return Hlt2Line( name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), jpsi], + algs=upfront_reconstruction() + [require_pvs(make_pvs()), jpsi], prescale=prescale, persistreco=persistreco, monitoring_variables=("m", "pt", "eta", "n_candidates"), @@ -166,11 +161,10 @@ def upsilon_to_mu_mu_line( name="Hlt2QEE_Upsilon1SToMuMu", prescale=1, persistreco=False ): """Upsilon(1S) decay to two muons line""" - pvs = make_pvs() - u1s = make_upsilon(pvs=pvs) + u1s = make_upsilon() return Hlt2Line( name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), u1s], + algs=upfront_reconstruction() + [require_pvs(make_pvs()), u1s], prescale=prescale, persistreco=persistreco, monitoring_variables=("m", "pt", "eta", "n_candidates"), diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/sexaquark.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/sexaquark.py index 091147cdca5d0b427a4e24e0e5c7aeb85b746a7a..fbb7b12cbff69edc6802260ebd8bf282ebb30d8e 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/sexaquark.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/sexaquark.py @@ -10,8 +10,12 @@ ############################################################################### """ Set of HLT2 lines to search for longlived Sexaquarks with detached annihilation vertices. -- S~ p+ -> Lambda~0 K+ (LLl, DDl, DDd) -- S~ n0 -> Lambda~0 KS0 (LLLL, LLDD, DDLL, DDDD) +- S~ p+/n0 -> Lambda~0 K+ X (LLl, DDl, DDd) +- S~ p+/n0 -> Lambda~0 KS0 X (LLLL, LLDD, DDLL, DDDD) +- S~ p+/n0 -> Lambda~0 Lambda~0 X (LLLL, LLDD, DDDD) +- S~ p+/n0 -> K+ K+ X- X (ll, dd) +- S~ p+/n0 -> KS0 K+ X- X (LLl, DDl, DDd) +- S~ p+/n0 -> KS0 KS0 X (LLLL, LLDD, DDDD) (prescaled) """ import Functors as F @@ -22,6 +26,7 @@ from GaudiKernel.SystemOfUnits import picosecond as ps from Moore.config import register_line_builder from Moore.lines import Hlt2Line from PyConf import configurable +from PyConf.Algorithms import SelectionFromRelationTable, WeightedRelTableAlg from RecoConf.algorithms_thor import ( ParticleCombiner, ParticleContainersMerger, @@ -41,25 +46,78 @@ from RecoConf.standard_particles import ( masses, ) -from Hlt2Conf.isolation import extra_outputs_for_isolation - all_lines = {} ######################### ### template builders ### ######################### +min_pid_k = 5 +min_prob_k = 0.4 +min_prob_p = 0.4 +track_chi2dof = 3 +vertex_chi2dof = 9 # For S, L0 and KS +minip = 6 * mm +minip_down = 3 * mm +minipchi2 = 36 +dm_L = 10 * MeV +dm_K = 36 * MeV +V0_END_VZ_VELO = 661 * mm # V0_LL needs to decay before 3rd last VELO module +V0_END_VZ_UT = 2300 * mm # V0_DD needs to decay before UT +dz_same = 5 * mm # V0 clearly needs to live before decaying in LLLL and DDDD lines +dz_diff = 200 * mm # V0_DD should decay further downstream than V0_LL in LLDD lines +max_npions = 20 # Maximum number of extra particles allowed to be collected per S + + +@configurable +def make_displaced_track(track): + """These cuts hold for both long and downstream tracks.""" + pvs = make_pvs() + code = F.require_all( + F.OWNPVIP > minip, + F.CHI2DOF < track_chi2dof, + F.MINIPCHI2(pvs) > minipchi2, + ) + return ParticleFilter(track, F.FILTER(code)) + + +def make_displaced_pion(is_down=False): + pion = make_down_pions_for_V0() if is_down else make_long_pions_for_V0() + return make_displaced_track(pion) + @configurable -def make_displaced_long_kaon(): - code = F.require_all(F.OWNPVIPCHI2 > 20000, F.PID_K > 3) - return ParticleFilter(make_has_rich_long_kaons(), F.FILTER(code)) +def make_displaced_kaon(is_down=False): + code = F.PID_K > min_pid_k + kaon = make_has_rich_down_kaons() if is_down else make_has_rich_long_kaons() + return ParticleFilter(make_displaced_track(kaon), F.FILTER(code)) @configurable -def make_displaced_down_kaon(): - code = F.require_all(F.OWNPVIPCHI2 > 100, F.PID_K > 3) - return ParticleFilter(make_has_rich_down_kaons(), F.FILTER(code)) +def make_displaced_V0(particles, is_down=False): + """These cuts hold for both Lambda and KS and both LL and DD streams.""" + pvs = make_pvs() + cos_angle = F.COSANGLE.bind( + F.THREEMOMENTUM @ F.CHILD(1, F.FORWARDARGS), F.THREEMOMENTUM @ F.FORWARDARGS + ) + sin_angle = fmath.sqrt(1 - fmath.pow(cos_angle, 2)) + code = F.require_all( + F.OWNPVIP > minip, + F.MINIPCHI2(pvs) > minipchi2, + F.OWNPVDIRA > 0, + F.CHILD(1, F.CHI2DOF) < track_chi2dof, + F.CHILD(2, F.CHI2DOF) < track_chi2dof, + F.CHILD(1, F.OWNPVIP) > (minip_down if is_down else minip), + F.CHILD(2, F.OWNPVIP) > (minip_down if is_down else minip), + F.END_VZ < (V0_END_VZ_UT if is_down else V0_END_VZ_VELO), + # Armenteros-Podolanski PT w.r.t. V0 cut + cos_angle < 1, # To prevent nan in next cut + F.CHILD(1, F.P) * sin_angle > 5 * MeV, + # Cut on 2nd child not effective after 1st + ) + if is_down: + code &= F.CHI2DOF < vertex_chi2dof + return ParticleFilter(particles, F.FILTER(code), name="qee_make_displaced_{hash}") @configurable @@ -72,30 +130,26 @@ def make_V0LambdaLL(): particles=[protons, pions], descriptors=descriptors, nominal_mass=masses["Lambda0"], - m_dmass=12 * MeV, - vchi2pdof_max=9, + m_dmass=dm_L, + vchi2pdof_max=vertex_chi2dof, bpvltime_min=None, ) @configurable -def make_displaced_LambdaLL(): - code = F.require_all(F.OWNPVIPCHI2 > 20000, F.OWNPVDIRA > 0) - return ParticleFilter( - make_V0LambdaLL(), F.FILTER(code), name="qee_make_displaced_LambdaLL_{hash}" - ) - - -@configurable -def make_displaced_LambdaDD(): +def make_displaced_L(is_down=False): + pvs = make_pvs() code = F.require_all( - F.CHI2DOF < 9, - F.OWNPVIPCHI2 > 100, - F.OWNPVDIRA > 0, - in_range(masses["Lambda0"] - 12 * MeV, F.MASS, masses["Lambda0"] + 12 * MeV), + F.CHILD(1, F.MINIPCHI2(pvs)) > minipchi2, # For proton only + F.CHILD(1, F.PROBNN_P) > min_prob_p, ) + if is_down: + code &= in_range(masses["Lambda0"] - dm_L, F.MASS, masses["Lambda0"] + dm_L) + Lambda = make_LambdaDD() if is_down else make_V0LambdaLL() return ParticleFilter( - make_LambdaDD(), F.FILTER(code), name="qee_make_displaced_LambdaDD_{hash}" + make_displaced_V0(Lambda, is_down), + F.FILTER(code), + name=f"qee_make_displaced_Lambda{'DD' if is_down else 'LL'}" + "_{hash}", ) @@ -107,30 +161,24 @@ def make_V0KsLL(): return _make_V0LL( particles=[pions, pions], descriptors=descriptors, - m_dmass=36 * MeV, - vchi2pdof_max=9, + m_dmass=dm_K, + vchi2pdof_max=vertex_chi2dof, bpvltime_min=None, ) @configurable -def make_displaced_KsLL(): - code = F.require_all(F.OWNPVIPCHI2 > 20000, F.OWNPVDIRA > 0) - return ParticleFilter( - make_V0KsLL(), F.FILTER(code), name="qee_make_displaced_KsLL_{hash}" - ) - - -@configurable -def make_displaced_KsDD(): - code = F.require_all( - F.CHI2DOF < 9, - F.OWNPVIPCHI2 > 100, - F.OWNPVDIRA > 0, - in_range(masses["KS0"] - 36 * MeV, F.MASS, masses["KS0"] + 36 * MeV), +def make_displaced_Ks(is_down=False): + code = ( + in_range(masses["KS0"] - dm_K, F.MASS, masses["KS0"] + dm_K) + if is_down + else F.ALL ) + Ks = make_KsDD() if is_down else make_V0KsLL() return ParticleFilter( - make_KsDD(), F.FILTER(code), name="qee_make_displaced_KsDD_{hash}" + make_displaced_V0(Ks, is_down), + F.FILTER(code), + name=f"qee_make_displaced_Ks{'DD' if is_down else 'LL'}" + "_{hash}", ) @@ -143,22 +191,39 @@ def make_sexaquark( name, descriptor, particles, - maxsdoca_cut=10 * mm, - extra_cuts=None, + maxsdoca=3 * mm, + DZ1=dz_same, + DZ2=dz_same, + min_dls=None, ): - combination_cut = F.require_all(F.MAXSDOCA < maxsdoca_cut) + combination_cut = F.require_all(F.MAXSDOCA < maxsdoca) composite_cut = F.require_all( F.END_VRHO > 3 * mm, - F.CHI2DOF < 9, + F.CHI2DOF < vertex_chi2dof, F.OWNPVDIRA > 0, F.OWNPVLTIME > 10 * ps, - _DZ_CHILD(1) > 2 * mm, ) - if descriptor == "[n0 -> Lambda~0 KS0]cc": - composite_cut &= _DZ_CHILD(2) > 2 * mm - - if extra_cuts is not None: - composite_cut &= extra_cuts + # It saves CPU to do PROBNN for kaons after Sexaquark vertex cut + + # First child: + if descriptor in ["[p+ -> K+ K+ pi-]cc"]: # 2 charged kaons + composite_cut &= F.CHILD(1, F.PROBNN_K) > min_prob_k + else: # 1 or 2 composite daughter particles + composite_cut &= _DZ_CHILD(1) > DZ1 + + # Second child: + if descriptor in [ + "[n0 -> Lambda~0 Lambda~0]cc", + "[n0 -> Lambda~0 KS0]cc", + "[n0 -> KS0 KS0]cc", + "n0 -> KS0 KS0", + ]: # 2 composite daughter particles + composite_cut &= _DZ_CHILD(2) > DZ2 + else: # 1 or 2 charged kaons + composite_cut &= F.CHILD(2, F.PROBNN_K) > min_prob_k + + if min_dls is not None: + composite_cut &= F.OWNPVDLS > min_dls return ParticleCombiner( Inputs=particles, @@ -170,100 +235,13 @@ def make_sexaquark( ) -####################### -### Sexaquark maker ### -####################### - - -### Proton interaction -@configurable -def make_S_LLl(): - return make_sexaquark( - name="qee_make_S_to_L0Kp_LLl_{hash}", - descriptor="[p+ -> Lambda~0 K+]cc", - particles=[make_displaced_LambdaLL(), make_displaced_long_kaon()], - maxsdoca_cut=0.1 * mm, - extra_cuts=F.OWNPVDLS > 25, - ) - - -@configurable -def make_S_DDl(): - return make_sexaquark( - name="qee_make_S_to_L0Kp_DDl_{hash}", - descriptor="[p+ -> Lambda~0 K+]cc", - particles=[make_displaced_LambdaDD(), make_displaced_long_kaon()], - maxsdoca_cut=3 * mm, - ) - - -@configurable -def make_S_DDd(): - return make_sexaquark( - name="qee_make_S_to_L0Kp_DDd_{hash}", - descriptor="[p+ -> Lambda~0 K+]cc", - particles=[make_displaced_LambdaDD(), make_displaced_down_kaon()], - maxsdoca_cut=5 * mm, - extra_cuts=F.OWNPVDLS > 15, - ) - - -### Neutron interaction -@configurable -def make_S_LLLL(): - return make_sexaquark( - name="qee_make_S_to_L0Ks_LLLL_{hash}", - descriptor="[n0 -> Lambda~0 KS0]cc", - particles=[make_displaced_LambdaLL(), make_displaced_KsLL()], - maxsdoca_cut=0.1 * mm, - extra_cuts=F.OWNPVDLS > 25, - ) - - -@configurable -def make_S_LLDD(): - return make_sexaquark( - name="qee_make_S_to_L0Ks_LLDD_{hash}", - descriptor="[n0 -> Lambda~0 KS0]cc", - particles=[make_displaced_LambdaLL(), make_displaced_KsDD()], - maxsdoca_cut=3 * mm, - ) - - -@configurable -def make_S_DDLL(): - return make_sexaquark( - name="qee_make_S_to_L0Ks_DDLL_{hash}", - descriptor="[n0 -> Lambda~0 KS0]cc", - particles=[make_displaced_LambdaDD(), make_displaced_KsLL()], - maxsdoca_cut=3 * mm, - ) - - -@configurable -def make_S_DDDD(): - return make_sexaquark( - name="qee_make_S_to_L0Ks_DDDD_{hash}", - descriptor="[n0 -> Lambda~0 KS0]cc", - particles=[make_displaced_LambdaDD(), make_displaced_KsDD()], - maxsdoca_cut=3 * mm, - extra_cuts=F.OWNPVDLS > 10, - ) - - ################### ### Extra pions ### ################### -import math - -LOG10_E = math.log10(math.e) - @configurable -def make_two_body_combination( - candidate, extra_particles, tes_name, use_fiducial_cut: bool = True -): +def make_two_body_combination(candidate, extra_particles, tes_name): """ Based on rd_isolation.make_two_body_combination() Make fake two body combination of S and extra pions. @@ -271,75 +249,76 @@ def make_two_body_combination( Args: candidate: Containers of reference particles tes_name: String for the TES location - use_fiducial_cut(bool): Use preselection requirement for the combination (default value True) Returns: TES location of two body combination of candidate and extra_particles """ + extra_particles = ParticleFilter( + extra_particles, F.FILTER(F.CHI2DOF < track_chi2dof) + ) - ## Set default values - pv_dist_min = 0.0 # mm - # cone_min = 0.25 #0.5**2 - log_ipchi2_wrtSV_max = 9 # 5 - - fiducial_cut_pv_dist = F.ALL - fiducial_cut_cone = F.ALL - fiducial_cut_sv_ipchi2 = F.ALL - - if use_fiducial_cut: - # displaced in z - fiducial_cut_pv_dist = ( - F.MAGNITUDE @ (F.ENDVERTEX_POS - F.OWNPV_POS) - ) * fmath.sign(F.END_VZ - F.OWNPVZ) > pv_dist_min - # outside cone - # DETA = F.CHILD(1, F.ETA) - F.CHILD(2, F.ETA) - # DPHI = F.ADJUST_ANGLE @ (F.CHILD(1, F.PHI) - F.CHILD(2, F.PHI)) - # fiducial_cut_cone = (DETA * DETA + DPHI * DPHI) > cone_min - # check wether S and pion tracks match - fiducial_cut_sv_ipchi2 = ( - fmath.log(F.IPCHI2.bind(F.CHILD(1, F.ENDVERTEX), F.CHILD(2, F.FORWARDARGS))) - * LOG10_E - < log_ipchi2_wrtSV_max - ) + # FD * sign(FD_z), i.e. displacement w.r.t. BPVZ of SPi + fiducial_cut_pv_dist = (F.MAGNITUDE @ (F.ENDVERTEX_POS - F.OWNPV_POS)) * fmath.sign( + F.END_VZ - F.OWNPVZ + ) > 0.0 * mm - # first combiner: Proton interaction with negative pions - comb_1 = ParticleCombiner( - Inputs=[candidate, extra_particles], - name="add_proton_pions_neg_{hash}", - DecayDescriptor="[D0 -> p+ pi-]cc", - CombinationCut=F.require_all(fiducial_cut_cone, fiducial_cut_sv_ipchi2), - CompositeCut=fiducial_cut_pv_dist, - ) - # second combiner: Proton interaction with positive pions - comb_2 = ParticleCombiner( - Inputs=[candidate, extra_particles], - name="add_proton_pions_pos_{hash}", - DecayDescriptor="[Delta++ -> p+ pi+]cc", - CombinationCut=F.require_all(fiducial_cut_cone, fiducial_cut_sv_ipchi2), - CompositeCut=fiducial_cut_pv_dist, - ) - # third combiner: Neutron interaction - comb_3 = ParticleCombiner( - Inputs=[candidate, extra_particles], - name="add_neutron_pions_{hash}", - DecayDescriptor="[D+ -> n0 pi+]cc", - CombinationCut=F.require_all(fiducial_cut_cone, fiducial_cut_sv_ipchi2), - CompositeCut=fiducial_cut_pv_dist, + # check wether S vertex and pion tracks match + fiducial_cut_sv_ipchi2 = ( + F.IPCHI2.bind(F.CHILD(1, F.ENDVERTEX), F.CHILD(2, F.FORWARDARGS)) + < vertex_chi2dof ) + + combiners = [ + ParticleCombiner( + Inputs=[candidate, extra_particles], + name=f"add_{comb_name}" + "_{hash}", + DecayDescriptor=f"[{dd}]cc", + CombinationCut=F.require_all(fiducial_cut_sv_ipchi2), + CompositeCut=fiducial_cut_pv_dist, + ) + for comb_name, dd in { + "proton_pions_neg": "D0 -> p+ pi-", + "proton_pions_pos": "Delta++ -> p+ pi+", + "neutron_pions_neg": "D- -> n0 pi-", + "neutron_pions_pos": "D+ -> n0 pi+", + }.items() + ] # merge combiners - comb = ParticleContainersMerger([comb_1, comb_2, comb_3], name=tes_name + "_{hash}") + return ParticleContainersMerger(combiners, name=tes_name + "_{hash}") + + +@configurable +def extra_outputs_for_isolation(name, ref_particles, extra_particles, selection): + """ + Save TES locations in extra outputs based on selection of extra particles information + Args: + name: Name of the TES location to store extra particles + ref_particles: Containers of reference particles + extra_particles: Containers of extra particles + selection: Expression to select combinations + """ + RelTableAlg = WeightedRelTableAlg( + InputCandidates=extra_particles, ReferenceParticles=ref_particles, Cut=selection + ) + + RelTable = RelTableAlg.OutputRelations - return comb + selection_to_persist = SelectionFromRelationTable(InputRelations=RelTable) + + extra_outputs = [(name, selection_to_persist.OutputLocation)] + + return ([RelTable], extra_outputs) @configurable def select_combinations_for_vertex_isolation( - name, candidate, extra_particles, max_two_body_vtx_cut: float = 9.0 + name, candidate, extra_particles, max_two_body_vtx_cut=vertex_chi2dof ): """ Based on rd_isolation.select_combinations_for_vertex_isolation() Add to the extra_outputs vertex isolations when adding one track to the vertex fit. - By default only two-body combinations with vertex fit chi2 smaller than 9. are selected. + By default only two-body combinations with vertex fit chi2 smaller than 'vertex_chi2dof' are selected. Returns TES location with two-body combinations for vertex isolation + in the form [('S_{long,down}_OneTrackCombination_VertexIsolation', DataHandle('/Event/SelectionFromWeightedRelationTable_{hash}/OutputLocation'))] Args: name: String with prefix of the TES location for extra selection @@ -355,116 +334,172 @@ def select_combinations_for_vertex_isolation( selection=( F.ABS @ (F.CHI2DOF() @ F.FORWARDARG1() - F.CHI2DOF() @ F.FORWARDARG0()) < max_two_body_vtx_cut - ), - ) # CHI2DOF instead of CHI2 - - -@configurable -def extra_long_pions(sexaquark): - return select_combinations_for_vertex_isolation( - name="S_long", - candidate=sexaquark, - extra_particles=make_long_pions_for_V0(), - max_two_body_vtx_cut=9.0, + ), # CHI2DOF instead of CHI2 ) @configurable -def extra_down_pions(sexaquark): +def extra_pions(sexaquarks, track_type: str): return select_combinations_for_vertex_isolation( - name="S_down", - candidate=sexaquark, - extra_particles=make_down_pions_for_V0(), - max_two_body_vtx_cut=9.0, + name=f"S_{track_type}", + candidate=sexaquarks, + extra_particles={ + "long": make_long_pions_for_V0, + "down": make_down_pions_for_V0, + }[track_type](), ) @configurable -def extra_pions(sexaquark): - pions = extra_long_pions(sexaquark) - pions += extra_down_pions(sexaquark) - return pions - - -##################### -### Line builders ### -##################### - - -### Proton interaction -@register_line_builder(all_lines) -def SpToL0Kp_LLl_line(name="Hlt2QEE_SexaquarkProtonToLambda0Kp_LLl", prescale=1.0): - alg = make_S_LLl() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_long_pions(alg), - ) - +def collect_extra_particles(sexaquarks, long_=True, down_=False): + """At least long_ or down_ should be true to be able to collect extra particles.""" + if long_ and down_: + pions_long = extra_pions(sexaquarks, "long") # (RelTable, extra_outputs) + pions_down = extra_pions(sexaquarks, "down") + return (pions_long[0] + pions_down[0], pions_long[1] + pions_down[1]) + elif long_: + return extra_pions(sexaquarks, "long") + else: + return extra_pions(sexaquarks, "down") -@register_line_builder(all_lines) -def SpToL0Kp_DDl_line(name="Hlt2QEE_SexaquarkProtonToLambda0Kp_DDl", prescale=1.0): - alg = make_S_DDl() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_long_pions(alg), - ) - - -@register_line_builder(all_lines) -def SpToL0Kp_DDd_line(name="Hlt2QEE_SexaquarkProtonToLambda0Kp_DDd", prescale=0.1): - alg = make_S_DDd() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_down_pions(alg), - ) +@configurable +def filter_npions(sexaquarks, pions, long_=True, down_=False): + """Filter away sexaquark candidates and the associated particles based on the number of particles associated to a sexaquark candidate""" + tables, _ = pions + + # MAP_INPUT_SIZE(Relations) = SIZE_OF @ RELATIONS.bind(TES(Relations), FORWARDARGS), where FORWARDARGS will be sexaquarks + if long_ and down_: + code = ( + F.VALUE_OR(0) @ (F.MAP_INPUT_SIZE(tables[0]) + F.MAP_INPUT_SIZE(tables[1])) + < max_npions + ) + else: + code = F.VALUE_OR(0) @ F.MAP_INPUT_SIZE(tables[0]) < max_npions -### Neutron interaction -@register_line_builder(all_lines) -def SnToL0Ks_LLLL_line(name="Hlt2QEE_SexaquarkNeutronToLambda0KS_LLLL", prescale=1.0): - alg = make_S_LLLL() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_long_pions(alg), + # Filter away sexaquarks: + good_sexaquarks = ParticleFilter( + sexaquarks, F.FILTER(code), name="n_pions_filter_{hash}" ) + # Get the extra particles from these sexaquarks: + good_pions = collect_extra_particles(good_sexaquarks, long_, down_)[1] + return good_sexaquarks, good_pions -@register_line_builder(all_lines) -def SnToL0Ks_LLDD_line(name="Hlt2QEE_SexaquarkNeutronToLambda0KS_LLDD", prescale=1.0): - alg = make_S_LLDD() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_long_pions(alg), - ) +##################### +### Line builders ### +##################### -@register_line_builder(all_lines) -def SnToL0Ks_DDLL_line(name="Hlt2QEE_SexaquarkNeutronToLambda0KS_DDLL", prescale=1.0): - alg = make_S_DDLL() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_long_pions(alg), - ) +from collections import namedtuple -@register_line_builder(all_lines) -def SnToL0Ks_DDDD_line(name="Hlt2QEE_SexaquarkNeutronToLambda0KS_DDDD", prescale=1.0): - alg = make_S_DDDD() - return Hlt2Line( - name=name, - algs=[require_pvs(make_pvs())] + [alg], - prescale=prescale, - extra_outputs=extra_pions(alg), - ) +LineConf = namedtuple( + "LineConf", + ["process", "stream", "prescale", "downstream"], +) +line_args = { + # L0Kp + "ProtonToLambda0Kp_LLl": LineConf("L0Kp", "LLl", 1.0, [False, False]), + "ProtonToLambda0Kp_DDl": LineConf("L0Kp", "DDl", 1.0, [True, False]), + "ProtonToLambda0Kp_DDd": LineConf("L0Kp", "DDd", 1.0, [True, True]), + # L0Ks + "NeutronToLambda0KS_LLLL": LineConf("L0Ks", "LLLL", 1.0, [False, False]), + "NeutronToLambda0KS_LLDD": LineConf("L0Ks", "LLDD", 1.0, [False, True]), + "NeutronToLambda0KS_DDLL": LineConf("L0Ks", "DDLL", 1.0, [True, False]), + "NeutronToLambda0KS_DDDD": LineConf("L0Ks", "DDDD", 1.0, [True, True]), + # L0L0 + "NucleonToLambda0Lambda0_LLLL": LineConf("L0L0", "LLLL", 1.0, [False, False]), + "NucleonToLambda0Lambda0_LLDD": LineConf("L0L0", "LLDD", 1.0, [False, True]), + "NucleonToLambda0Lambda0_DDDD": LineConf("L0L0", "DDDD", 1.0, [True, True]), + # KpKp + "NucleonToKpKpPim_ll": LineConf("KpKpPim", "ll", 0.25, [False, False, False]), + "NucleonToKpKpPim_dd": LineConf("KpKpPim", "dd", 0.5, [True, True, True]), + # KsKp + "NucleonToKS0KpPim_LLl": LineConf("KsKpPim", "LLl", 1.0, [False, False, False]), + "NucleonToKS0KpPim_DDl": LineConf("KsKpPim", "DDl", 1.0, [True, False, False]), + "NucleonToKS0KpPim_DDd": LineConf("KsKpPim", "DDd", 1.0, [True, True, True]), + # KsKs + "NucleonToKSKS_LLLL": LineConf("KsKs", "LLLL", 0.01, [False, False]), + "NucleonToKSKS_LLDD": LineConf("KsKscc", "LLDD", 0.01, [False, True]), + "NucleonToKSKS_DDDD": LineConf("KsKs", "DDDD", 0.01, [True, True]), +} +ProcessConf = namedtuple( + "ProcessConf", + ["descriptor", "particles"], +) +process_args = { + "L0Kp": ProcessConf( + "[p+ -> Lambda~0 K+]cc", [make_displaced_L, make_displaced_kaon] + ), + "L0Ks": ProcessConf( + "[p+ -> Lambda~0 KS0]cc", [make_displaced_L, make_displaced_Ks] + ), + "L0L0": ProcessConf( + "[p+ -> Lambda~0 Lambda~0]cc", [make_displaced_L, make_displaced_L] + ), + "KpKpPim": ProcessConf( + "[p+ -> K+ K+ pi-]cc", + [make_displaced_kaon, make_displaced_kaon, make_displaced_pion], + ), + "KsKpPim": ProcessConf( + "[n0 -> KS0 K+ pi-]cc", + [make_displaced_Ks, make_displaced_kaon, make_displaced_pion], + ), + "KsKs": ProcessConf("n0 -> KS0 KS0", [make_displaced_Ks, make_displaced_Ks]), + "KsKscc": ProcessConf("[n0 -> KS0 KS0]cc", [make_displaced_Ks, make_displaced_Ks]), +} +StreamConf = namedtuple( + "StreamConf", ["long_pions", "down_pions", "maxsdoca", "dz1", "dz2", "dls"] +) +stream_args = { + "LLl": StreamConf(True, False, 0.1 * mm, dz_same, None, 25), + "DDl": StreamConf(True, False, 3.0 * mm, dz_diff, None, None), + "DDd": StreamConf(False, True, 3.0 * mm, dz_same, None, 15), + "LLLL": StreamConf(True, False, 0.1 * mm, dz_same, dz_same, 25), + "LLDD": StreamConf(True, False, 3.0 * mm, dz_same, dz_diff, None), + "DDLL": StreamConf(True, False, 3.0 * mm, dz_diff, dz_same, None), + "DDDD": StreamConf(True, True, 3.0 * mm, dz_same, dz_same, 10), + "ll": StreamConf(True, False, 0.1 * mm, None, None, 25), + "dd": StreamConf(False, True, 3.0 * mm, None, None, 15), +} + +for sfx, line_config in line_args.items(): + + def create_sexaquark_line(sfx, line_config): + stream = stream_args[line_config.stream] + process = process_args[line_config.process] + + @register_line_builder(all_lines) + def sexaquark_line( + name=f"Hlt2QEE_Sexaquark{sfx}", prescale=line_config.prescale + ): + sexaquarks = make_sexaquark( + name=f"qee_make_S_to_{line_config.process}_{line_config.stream}" + + "_{hash}", + descriptor=process.descriptor, + particles=[ + process.particles[i](line_config.downstream[i]) + for i in range(len(line_config.downstream)) + ], + maxsdoca=stream.maxsdoca, + DZ1=stream.dz1, + DZ2=stream.dz2, + min_dls=stream.dls, + ) + pions = collect_extra_particles( + sexaquarks, stream.long_pions, stream.down_pions + ) + sexaquarks, pions = filter_npions( + sexaquarks, pions, stream.long_pions, stream.down_pions + ) + return Hlt2Line( + name=name, + algs=[require_pvs(make_pvs())] + [sexaquarks], + prescale=prescale, + extra_outputs=pions, + ) + + return sexaquark_line + + create_sexaquark_line(sfx, line_config) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/single_high_pt_muon.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/single_high_pt_muon.py index 8cc398e8ff54401648f3fd869b66cebaeb8a60a3..d55da7ce8d17fc45ad93b337cb9362e0c8fca884 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/single_high_pt_muon.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/single_high_pt_muon.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2019-2023 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2019-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". # @@ -82,6 +82,7 @@ def single_muon_highpt_line( prescale=prescale, persistreco=persistreco, monitoring_variables=("pt", "eta", "n_candidates"), + hlt1_filter_code=["Hlt1SingleHighPtMuonDecision"], ) @@ -100,6 +101,7 @@ def single_muon_highpt_prescale_line( prescale=prescale, persistreco=persistreco, monitoring_variables=("pt", "eta", "n_candidates"), + hlt1_filter_code=["Hlt1SingleHighPtMuonDecision"], ) @@ -120,6 +122,7 @@ def single_muon_highpt_iso_line( prescale=prescale, persistreco=persistreco, monitoring_variables=("pt", "eta", "n_candidates"), + hlt1_filter_code=["Hlt1SingleHighPtMuonDecision"], ) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/spruce_qee.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/spruce_qee.py index e79b229fc17a911541356d596b5847f416d1e8da..babebedf9a8ff4f940ef81f72c4eb9ea8d3e6d04 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/spruce_qee.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/spruce_qee.py @@ -22,10 +22,10 @@ from Moore.config import SpruceLine, register_line_builder from PyConf import configurable from PyConf.Algorithms import SelectionFromRelationTable, WeightedRelTableAlg from PyConf.reading import get_particles -from RecoConf import rdbuilder_thor from RecoConf.event_filters import require_pvs from RecoConf.reconstruction_objects import make_pvs, upfront_reconstruction +from Hlt2Conf.lines.qee.busca import make_DownstreamBuScaInclusive2H_DD from Hlt2Conf.lines.qee.diboson import ( make_Wleptgamma_cand, make_WW_emu_cand, @@ -48,6 +48,10 @@ from Hlt2Conf.lines.qee.high_mass_dielec import ( z_to_e_e_line, ) from Hlt2Conf.lines.qee.high_mass_dimuon import ( + A_to_mu_mu_line, + A_to_mu_mu_same_sign_line, + make_A_cand, + make_Ass_cand, make_Z_cand, make_Z_cand_DoubleNoMuID, make_Z_cand_SingleNoMuID, @@ -85,8 +89,11 @@ from Hlt2Conf.lines.qee.jets import ( make_TrijetsTwoSVTag_cand, ) from Hlt2Conf.lines.qee.qee_builders import ( + make_gamma_jet, make_qee_btokhh, make_qee_detached_dihadron, + make_qee_detached_kaons, + make_qee_detached_kstar0s, muon_filter, ) from Hlt2Conf.lines.qee.single_high_pt_electron import ( @@ -107,10 +114,8 @@ from Hlt2Conf.lines.qee.single_high_pt_electron import ( ) from Hlt2Conf.lines.qee.single_high_pt_muon import ( make_isolated_muons, - single_muon_highpt_iso_line, single_muon_highpt_line, single_muon_highpt_nomuid_line, - single_muon_highpt_prescale_line, ) from Hlt2Conf.lines.qee.single_high_pt_muon import threshold_map as muon_thresholds from Hlt2Conf.lines.qee.top_muon_elec import ( @@ -184,6 +189,21 @@ def z_to_mu_mu_sprucing_line(name="SpruceQEE_ZToMuMu", prescale=1): ) +@register_line_builder(sprucing_lines) +@configurable +def A_to_mu_mu_sprucing_line(name="SpruceQEE_AToMuMu", prescale=1): + """A decay to two muons line, both requiring ismuon, no HLT requirement""" + + line_alg = make_A_cand() + return SpruceLine( + name=name, + persistreco=True, + algs=upfront_reconstruction() + [line_alg], + hlt2_filter_code=_hlt2_decision_regex(A_to_mu_mu_line), + prescale=prescale, + ) + + @register_line_builder(sprucing_lines) @configurable def z_to_mu_mu_single_nomuid_sprucing_line( @@ -233,6 +253,47 @@ def same_sign_dimuon_sprucing_line(name="SpruceQEE_DiMuonSameSignHighMass", pres ) +@register_line_builder(sprucing_lines) +@configurable +def A_to_mu_mu_same_sign_sprucing_line(name="SpruceQEE_AToMuMuSameSign", prescale=1): + """A decay to two same sign muons line, no HLT requirement""" + + line_alg = make_Ass_cand() + return SpruceLine( + name=name, + persistreco=True, + algs=upfront_reconstruction() + [line_alg], + hlt2_filter_code=_hlt2_decision_regex(A_to_mu_mu_same_sign_line), + prescale=prescale, + ) + + +@register_line_builder(sprucing_lines) +@configurable +def busca_sprucing_line(name="SpruceQEE_BuSca", prescale=1): + line_alg = make_DownstreamBuScaInclusive2H_DD() + return SpruceLine( + name=name, + persistreco=True, + algs=upfront_reconstruction() + [line_alg], + hlt1_filter_code=["Hlt1DownstreamBuSca.*Decision"], + prescale=prescale, + ) + + +@register_line_builder(sprucing_lines) +@configurable +def busca_same_sign_sprucing_line(name="SpruceQEE_BuScaSameSign", prescale=1): + line_alg = make_DownstreamBuScaInclusive2H_DD(descriptors="[KS0 -> pi+ pi+]cc") + return SpruceLine( + name=name, + persistreco=True, + algs=upfront_reconstruction() + [line_alg], + hlt1_filter_code=["Hlt1DownstreamBuSca.*Decision"], + prescale=prescale, + ) + + @register_line_builder(sprucing_lines) @configurable def z_to_e_e_sprucing_line(name="SpruceQEE_ZToEE", prescale=1): @@ -265,6 +326,24 @@ def same_sign_dielec_sprucing_line( ) +################ Gamma + Jet lines ################ +for photon_type in ["DD", "LL"]: + + @register_line_builder(sprucing_lines) + @configurable + def gamma_jet_sprucing_line(name=f"SpruceQEE_Gamma{photon_type}Jet", prescale=1): + """Converted photon + jet line""" + + line_alg = make_gamma_jet(photon_type=photon_type) + return SpruceLine( + name=name, + persistreco=True, + algs=upfront_reconstruction() + [line_alg], + hlt2_filter_code=f"Hlt2QEE_Gamma{photon_type}JetFullDecision", + prescale=prescale, + ) + + ################ Z + Jet lines ################ @register_line_builder(sprucing_lines) @configurable @@ -1060,7 +1139,7 @@ def BuToKpDarkBoson_DarkBosontohh_sprucing_line( pvs = make_pvs() dihadrons = make_qee_detached_dihadron() - Kp = rdbuilder_thor.make_rd_detached_kaons(mipchi2dvprimary_min=25) + Kp = make_qee_detached_kaons(mipchi2dvprimary_min=25) Bp = make_qee_btokhh( intermediate_state=dihadrons, @@ -1087,7 +1166,7 @@ def B0ToKst0DarkBoson_DarkBosontohh_sprucing_line( pvs = make_pvs() dihadrons = make_qee_detached_dihadron() - Kst = rdbuilder_thor.make_rd_detached_kstar0s() + Kst = make_qee_detached_kstar0s() Bz = make_qee_btokhh( intermediate_state=dihadrons, diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/ttrack_llps.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/ttrack_llps.py index acfa4fcb5eb07b2ac1afe62983d66401d660b9a6..d4cac2be913b4d1cd8960567912d2c034a3cf514 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/ttrack_llps.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/ttrack_llps.py @@ -13,13 +13,15 @@ Define HLT2 lines for T track LLP searches. """ import Functors as F +from GaudiKernel.SystemOfUnits import MeV from Moore.config import register_line_builder from Moore.lines import Hlt2Line +from Moore.streams import DETECTORS from PyConf import configurable from RecoConf.event_filters import require_pvs -from RecoConf.rdbuilder_thor import make_rd_detached_kstar0s, make_rd_detached_muons from RecoConf.reconstruction_objects import make_pvs, upfront_reconstruction from RecoConf.standard_particles import ( + make_ismuon_long_muon, make_long_electrons_with_brem, make_long_muons, make_ttrack_muons, @@ -27,9 +29,13 @@ from RecoConf.standard_particles import ( ) from Hlt2Conf.lines.qee.qee_builders import ( + filter_ttrack_muons_for_high_mass, + filter_ttrack_pions_for_high_mass, make_BToLHNL_TT, make_HNL_TT, make_long_lepton_forHNL, + make_qee_detached_kstar0s, + make_qee_detached_muons, make_ttrack_forHNL, make_XtoTT_kaons, make_XtoTT_muons, @@ -37,6 +43,7 @@ from Hlt2Conf.lines.qee.qee_builders import ( qee_BtoLX_TT, qee_filtered_long_kaons, qee_set_max_pvs, + qee_ttrack_combiner_highmass, ) turbo_lines = {} @@ -57,7 +64,6 @@ def qee_BtoKH_KK_exclTT(name="Hlt2QEE_BtoKH_KK_exclTT", prescale=0.008): long_particles=filtered_long_kaons, ttrack_particles=dikaon, decay_descriptor="[B+ -> K+ KS0]cc", - pvs=pvs, ) return Hlt2Line( name=name, @@ -71,13 +77,12 @@ def qee_BtoKH_KK_exclTT(name="Hlt2QEE_BtoKH_KK_exclTT", prescale=0.008): @configurable def qee_BtoKstar0H_KK_exclTT(name="Hlt2QEE_BtoKstar0H_KK_exclTT", prescale=0.01): pvs = make_pvs() - long_kstars = make_rd_detached_kstar0s() + long_kstars = make_qee_detached_kstar0s() dikaon = make_XtoTT_kaons(pvs) b_candidate = qee_BtoLX_TT( long_particles=long_kstars, ttrack_particles=dikaon, decay_descriptor="[B0 -> K*(892)0 KS0]cc", - pvs=pvs, ) return Hlt2Line( name=name, @@ -97,7 +102,6 @@ def qee_BtoKH_MuMu_exclTT(name="Hlt2QEE_BtoKH_MuMu_exclTT", prescale=1.0): long_particles=filtered_long_kaons, ttrack_particles=dimuon, decay_descriptor="[B+ -> K+ KS0]cc", - pvs=pvs, ) return Hlt2Line( name=name, @@ -111,13 +115,12 @@ def qee_BtoKH_MuMu_exclTT(name="Hlt2QEE_BtoKH_MuMu_exclTT", prescale=1.0): @configurable def qee_BtoKstar0H_MuMu_exclTT(name="Hlt2QEE_BtoKstar0H_MuMu_exclTT", prescale=1.0): pvs = make_pvs() - long_kstars = make_rd_detached_kstar0s() + long_kstars = make_qee_detached_kstar0s() dimuon = make_XtoTT_muons(pvs) b_candidate = qee_BtoLX_TT( long_particles=long_kstars, ttrack_particles=dimuon, decay_descriptor="[B0 -> K*(892)0 KS0]cc", - pvs=pvs, ) return Hlt2Line( name=name, @@ -152,13 +155,12 @@ def qee_HtoMuMu_TT(name="Hlt2QEE_HtoMuMu_TTFull", prescale=1.0): @configurable def qee_BtoMuN_PiMu_exclTT(name="Hlt2QEE_BtoMuN_PiMu_exclTT", prescale=1.0): pvs = make_pvs() - long_muons = make_rd_detached_muons() + long_muons = make_qee_detached_muons() hnl = make_XtoTT_pimu(pvs) b_candidate = qee_BtoLX_TT( long_particles=long_muons, ttrack_particles=hnl, decay_descriptor="[B+ -> mu+ KS0]cc", - pvs=pvs, ) return Hlt2Line( name=name, @@ -172,14 +174,13 @@ def qee_BtoMuN_PiMu_exclTT(name="Hlt2QEE_BtoMuN_PiMu_exclTT", prescale=1.0): @configurable def qee_BctoMuN_PiMu_exclTT(name="Hlt2QEE_BctoMuN_PiMu_exclTT", prescale=1.0): pvs = make_pvs() - long_muons = make_rd_detached_muons() + long_muons = make_qee_detached_muons() hnl = make_XtoTT_pimu(pvs) b_candidate = qee_BtoLX_TT( long_particles=long_muons, ttrack_particles=hnl, decay_descriptor="[B_c+ -> mu+ KS0]cc", - pvs=pvs, mass_min=5200.0, mass_max=7300.0, ) @@ -194,98 +195,117 @@ def qee_BctoMuN_PiMu_exclTT(name="Hlt2QEE_BctoMuN_PiMu_exclTT", prescale=1.0): ## HNL Inclusive Lines @register_line_builder(turbo_lines) @configurable -def BtoMuN_MuPi_InclTT(name="Hlt2QEE_BtoMuN_MuPi_InclTT", prescale=0.05): +def qee_NtoPiMu_inclTT(name="Hlt2QEE_N_PiMu_inclTT", prescale=1): + """A line to select the [N -> pi+ mu-]CC decay for an inclusive selection + for the [B -> X mu (N -> pi mu)] decay""" + pvs = make_pvs() - long_muons = make_long_lepton_forHNL( - make_long=make_long_muons, - name="filter_long_muons_forHNL_{hash}", - pid=F.require_all( - F.ISMUON, - F.PID_MU > 2.0, - (F.PID_MU - F.PID_P) > 2.0, - (F.PID_MU - F.PID_K) > 2.0, - (F.PID_MU - F.PID_E) > 2.0, - ), - ) - filtered_Tpions = make_ttrack_forHNL( - make_ttrack=make_ttrack_pions, - name="filter_Ttrack_pions_forHLN_{hash}", - pid=F.require_all(F.PID_P < 2.0, F.PID_K < 2.0, F.PID_E < 2.0), + TT_pions = filter_ttrack_pions_for_high_mass( + pvs, + pt_min=750.0 * MeV, + p_min=10000.0 * MeV, + pid_p_max=1000.0, + pid_k_max=1000.0, + chi2_max=12.0, + not_ismuon=False, ) - filtered_Tmuons = make_ttrack_forHNL( - make_ttrack=make_ttrack_muons, - name="filter_Ttrack_muons_forHLN_{hash}", - # F.ISMUON not inserted because the muon reconstruction does not yet include T tracks - pid=F.require_all( - (F.PID_MU - F.PID_P) > 2.0, - (F.PID_MU - F.PID_K) > 2.0, - (F.PID_MU - F.PID_E) > 2.0, - ), - ) - hnl = make_HNL_TT( - pions=filtered_Tpions, - leptons=filtered_Tmuons, - name="HNL_TT_combiner_MuPi_{hash}", - descriptor="[KS0 -> mu- pi+]cc", + + TT_muons = filter_ttrack_muons_for_high_mass( + pvs, + ghostprob_max=0.4, + pt_min=750.0 * MeV, + p_min=10000.0 * MeV, + chi2_max=12.0, + pid_mu_min=-5.0, + muondll_mu_min=-1.0, + muonchi2_max=5.0, + pid_k_max=1000.0, # Proxy for infinity + pid_p_max=1000.0, # Proxy for infinity + pid_mu_diff_max=None, ) - bp = make_BToLHNL_TT( - long_leptons=long_muons, - hnl_tt=hnl, - name="B_combiner_MuN_MuPiTT_{hash}", - descriptor="[B+ -> mu+ KS0]cc", + + hnls = qee_ttrack_combiner_highmass( + TT_pions, + TT_muons, + "[KS0 -> pi+ mu-]cc", + ownpv_dira_min=0.999, + max_chi2=12.0, + mass_min=1400.0 * MeV, + mass_max=8000.0 * MeV, + pt_min=1500.0 * MeV, + ownpv_ip_max=100.0, + ownpv_ip_chi2_max=20.0, + use_corr_mass=False, ) + + long_muons = ( + make_ismuon_long_muon() + ) # Long muons passing ISMUON for offline combinations + return Hlt2Line( name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), long_muons, bp], + algs=upfront_reconstruction() + [require_pvs(pvs), qee_set_max_pvs(pvs), hnls], prescale=prescale, + raw_banks=DETECTORS, + extra_outputs=[("ISMUON_long_muons", long_muons)], ) @register_line_builder(turbo_lines) @configurable -def BtoEN_MuPi_InclTT(name="Hlt2QEE_BtoEN_MuPi_InclTT", prescale=0.1): +def qee_NtoPiMu_SS_inclTT(name="Hlt2QEE_N_PiMu_SS_inclTT", prescale=0.1): + """A line to select the [N -> pi+ mu+]CC (i.e. same sign; SS) decay as a background control selection + for the [B -> X mu (N -> pi mu)] decay""" + pvs = make_pvs() - long_electrons = make_long_lepton_forHNL( - make_long=make_long_electrons_with_brem, - name="filter_long_electrons_forHNL_{hash}", - pid=F.require_all( - F.PID_E > 2.0, - (F.PID_E - F.PID_P) > 2.0, - (F.PID_E - F.PID_K) > 2.0, - (F.PID_E - F.PID_MU) > 2.0, - ~F.ISMUON, - ), - ) - filtered_Tpions = make_ttrack_forHNL( - make_ttrack=make_ttrack_pions, - name="filter_Ttrack_pions_forHLN_{hash}", - pid=F.require_all(F.PID_P < 2.0, F.PID_K < 2.0, F.PID_E < 2.0), - ) - filtered_Tmuons = make_ttrack_forHNL( - make_ttrack=make_ttrack_muons, - name="filter_Ttrack_muons_forHLN_{hash}", - # F.ISMUON not inserted because the muon reconstruction does not yet include T tracks - pid=F.require_all( - (F.PID_MU - F.PID_P) > 2.0, - (F.PID_MU - F.PID_K) > 2.0, - (F.PID_MU - F.PID_E) > 2.0, - ), + + TT_pions = filter_ttrack_pions_for_high_mass( + pvs, + pt_min=750.0 * MeV, + p_min=10000.0 * MeV, + pid_p_max=1000.0, + pid_k_max=1000.0, + chi2_max=12.0, + not_ismuon=False, ) - hnl = make_HNL_TT( - pions=filtered_Tpions, - leptons=filtered_Tmuons, - name="HNL_TT_combiner_MuPi_{hash}", - descriptor="[KS0 -> mu- pi+]cc", + + TT_muons = filter_ttrack_muons_for_high_mass( + pvs, + ghostprob_max=0.4, + pt_min=750.0 * MeV, + p_min=10000.0 * MeV, + chi2_max=12.0, + pid_mu_min=-5.0, + muondll_mu_min=-1.0, + muonchi2_max=5.0, + pid_k_max=1000.0, # Proxy for infinity + pid_p_max=1000.0, # Proxy for infinity + pid_mu_diff_max=None, ) - bp = make_BToLHNL_TT( - long_leptons=long_electrons, - hnl_tt=hnl, - name="B_combiner_EN_MuPiTT_{hash}", - descriptor="[B+ -> e+ KS0]cc", + + hnls = qee_ttrack_combiner_highmass( + TT_pions, + TT_muons, + "[KS0 -> pi+ mu+]cc", + ownpv_dira_min=0.999, + max_chi2=12.0, + mass_min=1400.0 * MeV, + mass_max=8000.0 * MeV, + pt_min=1500.0 * MeV, + ownpv_ip_max=100.0, + ownpv_ip_chi2_max=20.0, + use_corr_mass=False, ) + + long_muons = ( + make_ismuon_long_muon() + ) # Long muons passing ISMUON for offline combinations + return Hlt2Line( name=name, - algs=upfront_reconstruction() + [require_pvs(pvs), long_electrons, bp], + algs=upfront_reconstruction() + [require_pvs(pvs), qee_set_max_pvs(pvs), hnls], prescale=prescale, + raw_banks=DETECTORS, + extra_outputs=[("ISMUON_long_muons", long_muons)], ) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/wz_boson_rare_decays.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/wz_boson_rare_decays.py index 64d73ac86d38d08a7aae2406ae5ffe8d045fc18f..70f3e2b75a5cffc0ad8916814f27cd0a5e6f6994 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/wz_boson_rare_decays.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/qee/wz_boson_rare_decays.py @@ -71,7 +71,6 @@ def make_EW_protons(pvs, p_min=1 * GeV, min_pt=1 * GeV, p_pidp_min=-5, mipchi2_m @configurable def make_EW_detached_Jpsi( - pvs, name="EW_Detached_Jpsi_{hash}", minIPChi2_muon=4, ownpvdls_min=9.0, @@ -365,7 +364,7 @@ def make_EW_B0ToJPsiKst( ownpvfdchi2_min=10.0, lifetime=0.2 * picosecond, ): - jpsi = make_EW_detached_Jpsi(pvs, minIPChi2_muon=mipchi2_min) + jpsi = make_EW_detached_Jpsi(minIPChi2_muon=mipchi2_min) kst = make_EW_KstToKPi(pvs, mipchi2_min=mipchi2_min) combination_code = F.require_all(in_range(comb_m_min, F.MASS, comb_m_max)) @@ -398,7 +397,7 @@ def make_EW_BsToJPsiPhi( ownpvfdchi2_min=10.0, lifetime=0.2 * picosecond, ): - jpsi = make_EW_detached_Jpsi(pvs, minIPChi2_muon=mipchi2_min) + jpsi = make_EW_detached_Jpsi(minIPChi2_muon=mipchi2_min) phi = make_EW_PhiToKK(pvs, mipchi2_min=mipchi2_min) combination_code = F.require_all(in_range(comb_m_min, F.MASS, comb_m_max)) @@ -431,7 +430,7 @@ def make_EW_BpToJPsiK( ownpvfdchi2_min=10.0, lifetime=0.2 * picosecond, ): - jpsi = make_EW_detached_Jpsi(pvs, minIPChi2_muon=mipchi2_min) + jpsi = make_EW_detached_Jpsi(minIPChi2_muon=mipchi2_min) kaons = make_EW_kaons(pvs, mipchi2_min=mipchi2_min) combination_code = F.require_all(in_range(comb_m_min, F.MASS, comb_m_max)) @@ -464,7 +463,7 @@ def make_EW_BcToJPsiPi( ownpvfdchi2_min=10.0, lifetime=0.1 * picosecond, ): - jpsi = make_EW_detached_Jpsi(pvs, minIPChi2_muon=mipchi2_min) + jpsi = make_EW_detached_Jpsi(minIPChi2_muon=mipchi2_min) pions = make_EW_pions(pvs, mipchi2_min=mipchi2_min) combination_code = F.require_all(in_range(comb_m_min, F.MASS, comb_m_max)) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/standard_jets.py b/Hlt/Hlt2Conf/python/Hlt2Conf/standard_jets.py index dc3bf5882b0c7cb841a5eb6b3dd5dddc3b675ce0..26340156fa98ed43eacb09d4eb1996f466118c49 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/standard_jets.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/standard_jets.py @@ -59,7 +59,7 @@ def make_topo_v0s(particles, pvs, bpvltime_min=0.0 * picosecond): displacement cut. """ ## TODO: Eliminate this step if it remains redundant - code = F.require_all(F.BPVLTIME(pvs) > bpvltime_min) + code = F.require_all(F.OWNPVLTIME > bpvltime_min) return ParticleFilter(particles, F.FILTER(code)) @@ -127,13 +127,12 @@ def make_topo_2body( F.PT > apt_min, F.MASS() < am_max, F.MAXSDOCACHI2CUT(adocachi2_max), - F.SUM(F.IS_ABS_ID("K+") & (F.BPVIPCHI2(pvs) < ipchi2_max_an)) - < an_ipchi2_max_an, + F.SUM(F.IS_ABS_ID("K+") & (F.OWNPVIPCHI2 < ipchi2_max_an)) < an_ipchi2_max_an, ) vertex_code = F.require_all( F.CHI2DOF() < adocachi2_max, - F.BPVFDCHI2(pvs) > bpvvdchi2_min, - Fmath.in_range(bpveta_min, F.BPVETA(pvs), bpveta_max), + F.OWNPVFDCHI2 > bpvvdchi2_min, + Fmath.in_range(bpveta_min, F.OWNPVETA, bpveta_max), ) inputs = prepare_topo_particles(pvs) @@ -176,16 +175,15 @@ def make_topo_2body_with_svtag( F.PT > apt_min, F.MASS() < am_max, F.MAXSDOCACHI2CUT(adocachi2_max), - F.SUM(F.IS_ABS_ID("K+") & (F.BPVIPCHI2(pvs) < ipchi2_max_an)) - < an_ipchi2_max_an, + F.SUM(F.IS_ABS_ID("K+") & (F.OWNPVIPCHI2 < ipchi2_max_an)) < an_ipchi2_max_an, ) vertex_code = F.require_all( F.MINTREE(F.IS_ABS_ID("K+"), F.GHOSTPROB()) < prod_ghostprob_max, F.MINTREE(F.IS_ABS_ID("K+"), F.MINIPCHI2(pvs)) > prod_ipchi2_min, F.MINTREE(F.ALL, F.PT) > prod_pt_min, F.CHI2DOF() < adocachi2_max, - Fmath.in_range(bpveta_min, F.BPVETA(pvs), bpveta_max), - F.BPVFDCHI2(pvs) > bpvvdchi2_min, + Fmath.in_range(bpveta_min, F.OWNPVETA, bpveta_max), + F.OWNPVFDCHI2 > bpvvdchi2_min, ) inputs = prepare_topo_particles(pvs) @@ -321,7 +319,7 @@ def make_trackjets( @configurable -def make_jets(name="SimpleJets_{hash}", pt_min=10 * GeV, JetsByVtx=True, tags=None): +def get_seljets(JetsByVtx=True, tags=None, name="GetSelJets_{hash}"): pflow = make_particleflow() jets = build_jets(pflow, JetsByVtx, name="JetBuilder" + name) @@ -329,9 +327,15 @@ def make_jets(name="SimpleJets_{hash}", pt_min=10 * GeV, JetsByVtx=True, tags=No taggedjets = tag_jets(jets, tags, useflightdirection=True, name="Tags" + name) jets = taggedjets - code = F.require_all( - F.IS_ABS_ID("CELLjet"), F.PT > pt_min, F.NINGENERATION(F.CHARGE != 0, 1) > 0 - ) + code = F.require_all(F.IS_ABS_ID("CELLjet"), F.NINGENERATION(F.CHARGE != 0, 1) > 0) + return ParticleFilter(jets, F.FILTER(code), name=name) + + +@configurable +def make_jets(name="SimpleJets_{hash}", pt_min=10 * GeV, JetsByVtx=True, tags=None): + jets = get_seljets(JetsByVtx, tags, name="GetSelJets" + name) + + code = F.require_all(F.PT > pt_min) return ParticleFilter(jets, F.FILTER(code), name=name)