diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/rd/builders/rdbuilder_thor.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/rd/builders/rdbuilder_thor.py new file mode 100644 index 0000000000000000000000000000000000000000..bce8cb7b84b8cfc28c2adf7e9d6c7a56256824e0 --- /dev/null +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/rd/builders/rdbuilder_thor.py @@ -0,0 +1,731 @@ +############################################################################### +# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # +# # +# This software is distributed under the terms of the GNU General Public # +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # +# # +# In applying this licence, CERN does not waive the privileges and immunities # +# granted to it by virtue of its status as an Intergovernmental Organization # +# or submit itself to any jurisdiction. # +############################################################################### +""" +Definition of RD builders for ThOr framework. +For now does not use any MVA tools as they are not ThOr-friendly. +""" +from GaudiKernel.SystemOfUnits import GeV, MeV, mm, ps + +from RecoConf.reconstruction_objects import make_pvs_v2 as make_pvs + +import Functors as F +from Functors.math import in_range + +from Hlt2Conf.algorithms_thor import require_all, ParticleCombiner, ParticleFilter + +from PyConf import configurable + +from Hlt2Conf.standard_particles import ( + make_has_rich_long_pions, + make_has_rich_long_kaons, + make_has_rich_long_protons, + make_long_pions, + make_long_kaons, + make_long_protons, + #make_has_rich_down_pions, make_has_rich_down_kaons, make_has_rich_down_protons, # we don't usually need RICH info in fact for D tracks + make_down_pions, + make_down_kaons, + make_down_protons, + #make_KsLL, make_KsDD, make_LambdaLL, make_LambdaDD, # we build our own versions below + make_long_muons, + make_long_electrons_with_brem, + make_photons, + make_resolved_pi0s, + make_merged_pi0s, + make_detached_mumu, + make_detached_dielectron_with_brem, + make_detached_mue_with_brem) + +#################################### +# Charged hadron selections # +#################################### + + +@configurable +def make_filter_tracks( + make_particles=make_has_rich_long_pions, + make_pvs=make_pvs, + name="rd_has_rich_long_pions", + pt_min=250. * MeV, + p_min=2.0 * GeV, + trchi2dof_max=3, #TBC with Reco + trghostprob_max=0.4, #TBC with Reco + mipchi2dvprimary_min=None, + pid=None): + + code = require_all(F.PT > pt_min, F.P > p_min, F.CHI2DOF < trchi2dof_max, + F.GHOSTPROB < trghostprob_max) + + if pid is not None: + code &= pid + + if mipchi2dvprimary_min is not None: + pvs = make_pvs() + code &= F.MINIPCHI2(pvs) > mipchi2dvprimary_min + + return ParticleFilter(make_particles(), name=name, Cut=F.FILTER(code)) + + +#################################### +# Detached # +#################################### + +# @configurable +# def make_rd_detached_tracks(name="rd_detached_tracks", +# mipchi2dvprimary_min=3.): +# """ +# Return RD detached tracks. +# """ +# return make_filter_tracks( +# name=name, +# make_particles=make_long_pions, +# mipchi2dvprimary_min=mipchi2dvprimary_min) + + +@configurable +def make_rd_detached_muons( + name="rd_detached_muons", + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_MU > 0.)): + """ + Return RD detached muons. + """ + return make_filter_tracks( + name=name, + make_particles=make_long_muons, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid) + + +@configurable +def make_rd_detached_electrons( + name="rd_detached_electrons", + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_E > 0.)): + """ + Return RD detached electrons with brem recovery. + """ + return make_filter_tracks( + name=name, + make_particles=make_long_electrons_with_brem, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid) + + +@configurable +def make_rd_detached_pions( + name="rd_detached_pions", + p_min=2 * GeV, + pt_min=250 * MeV, + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_K <= 2.)): + """ + Return RD 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_rd_detached_kaons( + name="rd_detached_kaons", + p_min=2 * GeV, + pt_min=250 * MeV, + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_K > -2.)): + """ + Return RD 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_rd_detached_protons( + name="rd_detached_protons", + p_min=2. * GeV, + pt_min=250 * MeV, + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_P > -2.)): + """ + Return RD detached protons. + """ + return make_filter_tracks( + name=name, + make_particles=make_long_protons, + p_min=p_min, + pt_min=pt_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid) + + +@configurable +def make_rd_has_rich_detached_pions( + name="rd_has_rich_detached_pions", + p_min=2 * GeV, + pt_min=250 * MeV, + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_K <= 0.)): + """ + Return RD 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_rd_has_rich_detached_kaons( + name="rd_has_rich_detached_kaons", + p_min=2 * GeV, + pt_min=250 * MeV, + mipchi2dvprimary_min=3., #TBC + pid=(F.PID_K > 0.)): + """ + Return RD 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_rd_has_rich_detached_protons( + name="rd_has_rich_detached_protons", + p_min=8.5 * + GeV, #kaon RICH threshold below which we have no p-K separation + pt_min=250 * MeV, + mipchi2dvprimary_min=3., #TBC + pid=require_all(F.PID_P > 0., F.PID_P - F.PID_K > -2.)): + """ + Return RD detached protons with hasRich. + """ + return make_filter_tracks( + name=name, + make_particles=make_has_rich_long_protons, + p_min=p_min, + pt_min=pt_min, + mipchi2dvprimary_min=mipchi2dvprimary_min, + pid=pid) + + +#################################### +# Prompt # +#################################### + + +@configurable +def make_rd_prompt_pions(name="rd_prompt_pions", pid=(F.PID_K < 0.)): + """ + Return RD prompt pions. + """ + return make_filter_tracks( + make_particles=make_has_rich_long_pions, name=name, pid=pid) + + +@configurable +def make_rd_prompt_kaons(name="rd_prompt_kaons", pid=(F.PID_K > 0.)): + """ + Return RD prompt kaons. + """ + return make_filter_tracks( + make_particles=make_has_rich_long_kaons, name=name, pid=pid) + + +@configurable +def make_rd_prompt_protons(name="rd_prompt_protons", + p_min=10. * GeV, + pid=(F.PID_P > 0.)): + """ + Return RD prompt protons. + """ + return make_filter_tracks( + make_particles=make_has_rich_long_protons, + name=name, + p_min=p_min, + pid=pid) + + +#################################### +# Downstream tracks # +#################################### +# def make_down_tracks(name="rd_down_tracks", +# pid=None): #do we really need this one? +# """ +# Return RD downstream hadrons with pion mass hypothesis. +# """ +# return make_filter_tracks( +# make_particles=make_down_pions, name=name, pid=pid) + + +def make_rd_detached_down_pions( + name="rd_detached_down_pions", + #mipchi2dvprimary_min=3., #this does not work + pt_min=0. * MeV, + p_min=0 * GeV, + pid=None): + """ + Return RD downstream hadrons with pion mass hypothesis. + """ + return make_filter_tracks( + make_particles=make_down_pions, + name=name, + #mipchi2dvprimary_min=mipchi2dvprimary_min, + pt_min=pt_min, + p_min=pt_min, + pid=pid) + + +def make_rd_detached_down_kaons( + name="rd_detached_down_kaons", + #mipchi2dvprimary_min=3., #TBC + pt_min=0. * MeV, + p_min=0 * GeV, + pid=None): + """ + Return RD downstream hadrons with kaon mass hypothesis. + """ + return make_filter_tracks( + make_particles=make_down_kaons, + name=name, + #mipchi2dvprimary_min=mipchi2dvprimary_min, + pt_min=pt_min, + p_min=pt_min, + pid=pid) + + +def make_rd_detached_down_protons( + name="rd_detached_down_protons", + #mipchi2dvprimary_min=3., #TBC + pt_min=0. * MeV, + p_min=0 * GeV, + pid=None): + """ + Return RD downstream hadrons with proton mass hypothesis. + """ + return make_filter_tracks( + make_particles=make_down_protons, + name=name, + #mipchi2dvprimary_min=mipchi2dvprimary_min, + pt_min=pt_min, + p_min=pt_min, + pid=pid) + + +#################################### +# Neutral particles # +#################################### +# builders to be updated once the CL functor is implemented in ThOr. + + +@configurable +def make_rd_photons(name="rd_photons", + make_particles=make_photons, + cl_min=0.1, + et_min=250 * MeV, + e_min=0 * MeV): + """For the time being just a dummy selection""" + + code = F.PT > et_min + return ParticleFilter(make_particles(), F.FILTER(code), name=name) + + +@configurable +def make_rd_resolved_pi0s( + name="rd_resolved_pi0s", + make_particles=make_resolved_pi0s, + pt_min=250 * MeV, + cl_min=0, + # photon_args={ + # 'ConfLevelCut': 0.1, + # 'PtCut': 250. * MeV + # }, +): + """For the time being just a dummy selection""" + + # code = require_all('PT > {pt_min}', 'P > {p_min}').format( + # pt_min=pt_min, p_min=p_min) + # + # return ParticleFilter( + # make_particles(photon_args=photon_args), Code=code, name=name) + code = F.PT > pt_min + return ParticleFilter(make_particles(), F.FILTER(code)) + + +@configurable +def make_rd_merged_pi0s(name="rd_merged_pi0s", + make_particles=make_merged_pi0s, + pt_min=250 * MeV, + cl_min=0): + """For the time being just a dummy selection""" + + code = F.PT > pt_min + return ParticleFilter(make_particles(), F.FILTER(code), name=name) + + +#################################### +# V0 particles and alike # +#################################### +# they are in principle defined in the Hlt2 standard_particles, however there is no way to tune IPCHI2 cuts on their children with the current ThOr framework. + + +@configurable +def make_rd_ks0_lls( + name="rd_ks0_lls", + make_pions=make_rd_detached_pions, + make_pvs=make_pvs, + am_min=460 * MeV, # ~40 MeV window + am_max=540 * MeV, + pi_p_min=2 * GeV, + pi_pt_min=0 * GeV, # has to be 0 !!! + pi_ipchi2_min=9, + adocachi2cut=30., + bpvvdchi2_min=4., + bpvltime_min=1 * ps, + vchi2pdof_max=30.): + ''' + Build Long-Long KS0 candidates. Approximately corresponding to the Run2 + "StdVeryLooseKsLL" cuts. + ''' + + pions = make_pions( + p_min=pi_p_min, + pt_min=pi_pt_min, + mipchi2dvprimary_min=pi_ipchi2_min, + pid=None) # important to get rid of any PID cuts! + descriptor = 'KS0 -> pi+ pi-' + combination_code = require_all( + in_range(am_min, F.MASS, am_max), F.MAXDOCACHI2CUT(adocachi2cut)) + pvs = make_pvs() + vertex_code = require_all( + F.CHI2DOF < vchi2pdof_max, + #F.BPVLTIME(pvs) > bpvltime_min, + F.BPVFDCHI2(pvs) > bpvvdchi2_min) + return ParticleCombiner([pions, pions], + name=name, + DecayDescriptor=descriptor, + CombinationCut=combination_code, + CompositeCut=vertex_code) + + +@configurable +def make_rd_ks0_dds( + name="rd_ks0_dds", + make_pions=make_rd_detached_down_pions, + make_pvs=make_pvs, + am_min=420 * MeV, # ~75 MeV window + am_max=570 * MeV, + pi_p_min=2 * GeV, + pi_pt_min=0, # has to be 0 !!! + #pi_ipchi2_min=4, + adocachi2cut=30., + bpvvdchi2_min=4., + bpvltime_min=1 * ps, + vchi2pdof_max=25.): + ''' + Build Long-Long KS0 candidates. Approximately corresponding to the Run2 + "StdVeryLooseKsLL" cuts. + ''' + + pions = make_pions( + p_min=pi_p_min, + pt_min=pi_pt_min, + #mipchi2dvprimary_min=pi_ipchi2_min, + pid=None) + descriptor = 'KS0 -> pi+ pi-' + combination_code = require_all( + in_range(am_min, F.MASS, am_max), F.MAXDOCACHI2CUT(adocachi2cut)) + pvs = make_pvs() + vertex_code = require_all( + F.CHI2DOF < vchi2pdof_max, + #F.BPVLTIME(pvs) > bpvltime_min, + F.BPVFDCHI2(pvs) > bpvvdchi2_min) + return ParticleCombiner([pions, pions], + name=name, + DecayDescriptor=descriptor, + CombinationCut=combination_code, + CompositeCut=vertex_code) + + +@configurable +def make_rd_lambda_lls( + name="rd_lambda_lls", + make_protons=make_rd_detached_protons, + make_pions=make_rd_detached_pions, + make_pvs=make_pvs, + am_min=1076 * MeV, # ~40 MeV window + am_max=1156 * MeV, + pi_p_min=2 * GeV, + p_p_min=2 * GeV, + p_pt_min=0, # recommended to be 0 + pi_pt_min=0, # has to be 0 !!! + pi_ipchi2_min=6, + p_ipchi2_min=9, + adocachi2cut=30., + bpvvdchi2_min=4., + bpvltime_min=1 * ps, + vchi2pdof_max=30.): + ''' + Build Long-Long Lambda candidates. Approximately corresponding to the Run2 + "StdVeryLooseLambdaLL" cuts. + ''' + + protons = make_protons( + p_min=p_p_min, + pt_min=p_pt_min, + mipchi2dvprimary_min=p_ipchi2_min, + pid=None) # important to get rid of any PID cuts! + pions = make_pions( + p_min=pi_p_min, + pt_min=pi_pt_min, + mipchi2dvprimary_min=pi_ipchi2_min, + pid=None) + descriptor = '[Lambda0 -> p+ pi-]cc' + combination_code = require_all( + in_range(am_min, F.MASS, am_max), F.MAXDOCACHI2CUT(adocachi2cut)) + pvs = make_pvs() + vertex_code = require_all( + F.CHI2DOF < vchi2pdof_max, + #F.BPVLTIME(pvs) > bpvltime_min, + F.BPVFDCHI2(pvs) > bpvvdchi2_min) + return ParticleCombiner([protons, pions], + name=name, + DecayDescriptor=descriptor, + CombinationCut=combination_code, + CompositeCut=vertex_code) + + +@configurable +def make_rd_lambda_dds( + name="rd_lambda_dds", + make_protons=make_rd_detached_down_protons, + make_pions=make_rd_detached_down_pions, + make_pvs=make_pvs, + am_min=1050 * MeV, # ~65 MeV window + am_max=1180 * MeV, + pi_p_min=2 * GeV, + p_p_min=2 * GeV, + p_pt_min=0, # recommended to be 0 + pi_pt_min=0, # has to be 0 !!! + #pi_ipchi2_min=6, + #p_ipchi2_min=9, + adocachi2cut=30., + bpvvdchi2_min=4., + bpvltime_min=1 * ps, + vchi2pdof_max=25.): + ''' + Build Down-Down Lambda candidates. Approximately corresponding to the Run2 + "StdVeryLooseLambdaLL" cuts. + ''' + + protons = make_protons( + p_min=p_p_min, + pt_min=p_pt_min, + #mipchi2dvprimary_min=p_ipchi2_min, + pid=None) # important to get rid of any PID cuts! + pions = make_pions( + p_min=pi_p_min, + pt_min=pi_pt_min, + #mipchi2dvprimary_min=pi_ipchi2_min, + pid=None) + descriptor = '[Lambda0 -> p+ pi-]cc' + combination_code = require_all( + in_range(am_min, F.MASS, am_max), F.MAXDOCACHI2CUT(adocachi2cut)) + pvs = make_pvs() + vertex_code = require_all( + F.CHI2DOF < vchi2pdof_max, + #F.BPVLTIME(pvs) > bpvltime_min, + F.BPVFDCHI2(pvs) > bpvvdchi2_min) + return ParticleCombiner([protons, pions], + name=name, + DecayDescriptor=descriptor, + CombinationCut=combination_code, + CompositeCut=vertex_code) + + +#################################### +# 2-body hadron combinations # +#################################### + + +@configurable +def make_rd_detached_kstar0s( + name="rd_detached_kstar0s", + make_rd_detached_pions=make_rd_has_rich_detached_pions, + make_rd_detached_kaons=make_rd_has_rich_detached_kaons, + make_pvs=make_pvs, + am_min=592 * MeV, + am_max=1192 * MeV, + pi_p_min=2 * GeV, + pi_pt_min=100 * MeV, + k_p_min=2 * GeV, + k_pt_min=100 * MeV, + kstar0_pt_min=400 * MeV, + adocachi2cut=30., + vchi2pdof_max=25): + ''' + Build Kstar0 candidates. Approximately corresponding to the Run2 + "StdVeryLooseDetachedKstar" cuts. + ''' + + pions = make_rd_detached_pions(p_min=pi_p_min, pt_min=pi_pt_min) + kaons = make_rd_detached_kaons(p_min=k_p_min, pt_min=k_pt_min) + descriptor = '[K*(892)0 -> K+ pi-]cc' + combination_code = require_all( + in_range(am_min, F.MASS, am_max), F.MAXDOCACHI2CUT(adocachi2cut)) + #pvs = make_pvs() + vertex_code = 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) + + +# this is in principle defined in the Hlt2 standard_particles, however there is no way to provide IPCHI2 cuts on the kaons within the current ThOr framework. +@configurable +def make_rd_detached_phis( + name="rd_detached_phis", + make_rd_detached_kaons=make_rd_has_rich_detached_kaons, + make_pvs=make_pvs, + am_max=1100 * MeV, #min is the di-kaon threshold anyway + k_p_min=2 * GeV, + k_pt_min=100 * MeV, + phi_pt_min=400 * MeV, + adocachi2cut=30., + vchi2pdof_max=25): + ''' + Build phi(1020) candidates. Approximately corresponding to the Run2 + "StdLooseDetachedPhi2KK" cuts. + ''' + + kaons = make_rd_detached_kaons(p_min=k_p_min, pt_min=k_pt_min) + descriptor = 'phi(1020) -> K+ K-' + combination_code = require_all(F.MASS < am_max, + F.MAXDOCACHI2CUT(adocachi2cut)) + #pvs = make_pvs() + vertex_code = require_all(F.CHI2DOF < vchi2pdof_max, F.PT > phi_pt_min) + return ParticleCombiner([kaons, kaons], + name=name, + DecayDescriptor=descriptor, + CombinationCut=combination_code, + CompositeCut=vertex_code) + + +#################################### +# Dileptons # +#################################### + + +@configurable +def filter_dimuon_noMVA(probnn_mu=0.0, max_dimuon_mass=6000 * MeV): + + dimuons = make_detached_mumu(probnn_mu=probnn_mu, pt_mu=150 * MeV) + code = require_all(F.MASS < max_dimuon_mass) + return ParticleFilter(dimuons, F.FILTER(code)) + + +@configurable +def filter_dielectron_noMVA(probnn_e=0.05, max_dielectron_mass=6000 * MeV): + + dielectrons = make_detached_dielectron_with_brem( + probnn_e=probnn_e, m_diE_max=max_dielectron_mass) + code = require_all(F.MASS < max_dielectron_mass) + return ParticleFilter(dielectrons, F.FILTER(code)) + + +@configurable +def filter_mue_noMVA(max_dilepton_mass=6000 * MeV, + min_probnn_mu=0.0, + min_PIDe=2., + same_sign=False): + + dileptons = make_detached_mue_with_brem( + min_probnn_mu=min_probnn_mu, min_PIDe=min_PIDe, same_sign=same_sign) + code = require_all(F.MASS < max_dilepton_mass) + return ParticleFilter(dileptons, F.FILTER(code)) + + +#################################### +# Tau builders # +#################################### + + +@configurable +def make_rd_tauons_hadronic_decay( + name="rd_tauons_hadronic_decay", + make_pions=make_rd_detached_pions, + descriptor="[tau+ -> pi- pi+ pi+]cc", + pi_pt_min=150 * MeV, + pi_ipchi2_min=4.0, + pi_pid=(F.PID_K < 8.), + best_pi_pt_min=300 * MeV, + best_pi_ipchi2_min=5, + pt_max=300 * MeV, + am_min=400 * MeV, + am_max=3500 * MeV, + make_pvs=make_pvs, + adoca_max=0.15 * mm, + am_2pi_max=1670 * MeV, + vchi2pdof_max=25, + bpvdira_min=0.99, +): + ''' + Build tau->3pi candidates. Approximately corresponding to the Run2 + "StdLooseDetachedTau" cuts. + ''' + + pions = make_pions( + pt_min=pi_pt_min, mipchi2dvprimary_min=pi_ipchi2_min, pid=pi_pid) + + combination12_code = require_all( + F.MASS < am_2pi_max, + F.DOCA(1, 2) < adoca_max, + ) + + pvs = make_pvs() + combination_code = require_all( + in_range(am_min, F.MASS, am_max), + F.DOCA(1, 3) < adoca_max, + F.DOCA(2, 3) < adoca_max, + F.SUM(F.PT > best_pi_pt_min) > 1, + F.SUM(F.MINIPCHI2(pvs) > best_pi_ipchi2_min) > 1, + ) + #TODO: add the M13<1670 cut once this is possible. + vertex_code = require_all( + in_range(am_min, F.MASS, am_max), + F.CHI2DOF < vchi2pdof_max, + F.BPVDIRA(pvs) > bpvdira_min, + ) + return ParticleCombiner([pions, pions, pions], + name=name, + DecayDescriptor=descriptor, + Combination12Cut=combination12_code, + CombinationCut=combination_code, + CompositeCut=vertex_code)