diff --git a/Hlt/Hlt2Conf/options/testrun_4bodies.py b/Hlt/Hlt2Conf/options/testrun_4bodies.py new file mode 100644 index 0000000000000000000000000000000000000000..8e229d7624bc6dd34c4af38fcb42699699cb54ad --- /dev/null +++ b/Hlt/Hlt2Conf/options/testrun_4bodies.py @@ -0,0 +1,49 @@ +############################################################################### +# (c) Copyright 2019 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. # +############################################################################### +"""Options for running HLT2 lines. + +Run like any other options file: + + ./Moore/run gaudirun.py hlt2_example.py +""" +from Moore import options, run_moore + +from Hlt2Conf.lines.D02HHHH import all_lines +from RecoConf.reco_objects_from_file import stateProvider_with_simplified_geom +# TODO stateProvider_with_simplified_geom must go away from option files + +input_files = [ + # MinBias 30000000 + # sim+std://MC/Upgrade/Beam7000GeV-Upgrade-MagDown-Nu7.6-25ns-Pythia8/Sim09c-Up02/Reco-Up01/30000000/LDST + # 'root://xrootd.echo.stfc.ac.uk/lhcb:prod/lhcb/MC/Upgrade/LDST/00069155/0000/00069155_00000878_2.ldst' + # D*-tagged D0 to KKpipi + 'root://ccxrootdlhcb.in2p3.fr//pnfs/in2p3.fr/data/lhcb/MC/Upgrade/MCFILTER.LDST/00047762/0000/00047762_00000001_1.mcfilter.ldst' +] + +options.input_files = input_files +options.input_type = 'ROOT' +options.input_raw_format = 4.3 +# When running from Upgrade MC, must use the post-juggling locations of the raw +# event + +options.evt_max = 100 +options.simulation = True +options.data_type = 'Upgrade' +options.dddb_tag = 'dddb-20171126' +options.conddb_tag = 'sim-20171127-vc-md100' + + +def make_lines(): + return [builder() for builder in all_lines.values()] + + +public_tools = [stateProvider_with_simplified_geom()] +run_moore(options, make_lines, public_tools) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/algorithms.py b/Hlt/Hlt2Conf/python/Hlt2Conf/algorithms.py index eed5bedfd921f74840b70b1eacf48820cdbca27d..a6c7bb69e567abd8f7939554ab064293f7ca47b0 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/algorithms.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/algorithms.py @@ -20,7 +20,9 @@ functional algorithms. from __future__ import absolute_import, division, print_function import string -from Configurables import CombineParticles, FilterDesktop +from Configurables import (CombineParticles, FilterDesktop, + DaVinci__N3BodyDecays as N3BodyDecays, + DaVinci__N4BodyDecays as N4BodyDecays) from RecoConf.hlt1_tracking import EmptyFilter from PyConf.components import make_algorithm @@ -28,10 +30,14 @@ from PyConf.components import make_algorithm __all__ = [ 'EmptyFilter', 'ParticleFilter', - 'ParticleCombiner', + 'ParticleCombiner' 'ParticleFilterWithPVs', 'ParticleCombinerWithPVs', 'require_all', + 'N3BodyCombiner', + 'N3BodyCombinerWithPVs' + 'N4BodyCombiner', + 'N4BodyCombinerWithPVs', ] @@ -112,6 +118,20 @@ combiners = { 4: make_dvalgorithm(CombineParticles, ninputs=4) } +threebodycombiners = { + 1: make_dvalgorithm(N3BodyDecays), + 2: make_dvalgorithm(N3BodyDecays, ninputs=2), + 3: make_dvalgorithm(N3BodyDecays, ninputs=3), + 4: make_dvalgorithm(N3BodyDecays, ninputs=4) +} + +fourbodycombiners = { + 1: make_dvalgorithm(N4BodyDecays), + 2: make_dvalgorithm(N4BodyDecays, ninputs=2), + 3: make_dvalgorithm(N4BodyDecays, ninputs=3), + 4: make_dvalgorithm(N4BodyDecays, ninputs=4) +} + def ParticleFilter(particles, **kwargs): """Return a filter algorithm that takes `particles` as inputs. @@ -129,7 +149,7 @@ def ParticleFilterWithPVs(particles, pvs, **kwargs): return ParticleFilter(particles=particles, PrimaryVertices=pvs, **kwargs) -def ParticleCombiner(particles, **kwargs): +def ParticleCombiner(particles, my_combiners=combiners, **kwargs): """Return a combiner algorithm that takes `particles` as inputs. Additional keyword arguments are forwarded to CombineParticles. @@ -138,10 +158,10 @@ def ParticleCombiner(particles, **kwargs): ninputs = len(particles) # Need to dispatch to the right combiner, based on the number of inputs - assert len( - combiners) >= ninputs, 'Do not have a combiner for {} inputs'.format( - ninputs) - combiner = combiners[ninputs] + assert len(my_combiners + ) >= ninputs, 'Do not have a combiner for {} inputs'.format( + ninputs) + combiner = my_combiners[ninputs] # Map each input container to an input property name inputs = { @@ -156,9 +176,45 @@ def ParticleCombiner(particles, **kwargs): return combiner(**kwargs).Output +def N3BodyCombiner(particles, **kwargs): + """Return a N3BodyDecays combiner algorithm that takes particles as inputs. + + Additional keyword arguments are forwarded to N3BodyDecays. + """ + return ParticleCombiner( + particles, my_combiners=threebodycombiners, **kwargs) + + +def N4BodyCombiner(particles, **kwargs): + """Return a N4BodyDecays combiner algorithm that takes particles as inputs. + + Additional keyword arguments are forwarded to N4BodyDecays. + """ + return ParticleCombiner( + particles, my_combiners=fourbodycombiners, **kwargs) + + def ParticleCombinerWithPVs(particles, pvs, **kwargs): """Return a combiner algorithm that takes `particles` and `pvs` as inputs. Additional keyword arguments are forwarded to CombineParticles. """ return ParticleCombiner(particles=particles, PrimaryVertices=pvs, **kwargs) + + +def N3BodyCombinerWithPVs(particles, pvs, **kwargs): + """Return a combiner algorithm that takes `particles` and `pvs` as inputs. + + Additional keyword arguments are forwarded to CombineParticles. + """ + ## TODO: eliminate duplication of code with ParticleCombinerWithPVs + return N3BodyCombiner(particles=particles, PrimaryVertices=pvs, **kwargs) + + +def N4BodyCombinerWithPVs(particles, pvs, **kwargs): + """Return a combiner algorithm that takes `particles` and `pvs` as inputs. + + Additional keyword arguments are forwarded to CombineParticles. + """ + ## TODO: eliminate duplication of code with ParticleCombinerWithPVs + return N4BodyCombiner(particles=particles, PrimaryVertices=pvs, **kwargs) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/D02HHHH.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/D02HHHH.py new file mode 100644 index 0000000000000000000000000000000000000000..8e13d13f5f63e34f9f551521cb1822282815dd43 --- /dev/null +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/D02HHHH.py @@ -0,0 +1,418 @@ +############################################################################### +# (c) Copyright 2019 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 some D0 -> h- h+ h- h+ HLT2 lines. + +The logic is the same as in Run 1/2: create HLT2 lines by defining selections +which create physics objects. These selections may consume other selections, as +filters or combiners. + +The change comes in how selection components are defined. + +TODO(AP): More docs. +""" +from __future__ import absolute_import, division, print_function +import math + +from Moore.config import HltLine, register_line_builder + +from GaudiKernel.SystemOfUnits import GeV, MeV, mm, mrad + +from RecoConf.hlt1_tracking import require_pvs, require_gec +from RecoConf.reco_objects_from_file import (make_pvs, upfront_reconstruction) + +from ..algorithms import (require_all, ParticleCombinerWithPVs, + ParticleFilterWithPVs, N4BodyCombinerWithPVs) +from ..framework import configurable +from ..standard_particles import (make_has_rich_long_pions as + make_has_rich_pions, make_has_rich_long_kaons + as make_has_rich_kaons) + +# Charged pion mass in MeV +_PION_M = 139.57061 * MeV # +/- 0.00024 + +all_lines = {} + + +@configurable +def make_selected_particles(make_particles, + make_pvs, + trchi2dof_max=4, + mipchi2_min=3.5, + pt_min=250 * MeV, + trg_ghost_prob_max=0.5, + pid=None): + # TODO(AP): this would be a whole lot nicer with f-strings (Python >= 3.6) + # An alternative is to pass **locals() to format, but... ew + code = require_all( + 'PT > {pt_min}', + 'TRGHOSTPROB < {trg_ghost_prob_max}', + # TODO(AP): Cut value is reasonable for Run 2, but removes basically + # everything in the upgrade sample + 'TRCHI2DOF < {trchi2dof_max}', + 'MIPCHI2DV(PRIMARY) > {mipchi2_min}').format( + pt_min=pt_min, + trg_ghost_prob_max=trg_ghost_prob_max, + trchi2dof_max=trchi2dof_max, + mipchi2_min=mipchi2_min) + if pid is not None: + code += ' & ({})'.format(pid) + return ParticleFilterWithPVs(make_particles(), make_pvs(), Code=code) + + +@configurable +def make_charm_pions(pid='PIDK > 0'): + return make_selected_particles( + make_particles=make_has_rich_pions, make_pvs=make_pvs, pid=pid) + + +@configurable +def make_charm_kaons(pid='PIDK < 0'): + return make_selected_particles( + make_particles=make_has_rich_kaons, make_pvs=make_pvs, pid=pid) + + +@configurable +def make_dzeros(particles, + descriptors, + pvs, + am_min=1730 * MeV, + am_max=2000 * MeV, + asumpt_min=1800 * MeV, + ap_min=25000 * MeV, + achi2doca_max=10.0, + vchi2pdof_max=12, + bpvvdchi2_min=25, + acos_bpvdira_max=20 * mrad): + + combination_code = require_all( + "in_range({am_min}, AM, {am_max})", + "(APT1 + APT2 + APT3 + APT4) > {asumpt_min}", "AP > {ap_min}", + "ACHI2DOCA(1,4) < {achi2doca_max}", "ACHI2DOCA(2,4) < {achi2doca_max}", + "ACHI2DOCA(3,4) < {achi2doca_max}").format( + am_min=am_min, + am_max=am_max, + asumpt_min=asumpt_min, + ap_min=ap_min, + achi2doca_max=achi2doca_max) + + ## TODO: Consider generalizing the hard-coded assumption in + ## sub-combination AM that the remaining particles will be pions. + ## If used for semileptonic reconstruction, will need to be changed. + combination123_code = require_all( + "AM < {am_max}", "ACHI2DOCA(1,3) < {achi2doca_max}", + "ACHI2DOCA(2,3) < {achi2doca_max}").format( + am_max=am_max - _PION_M, achi2doca_max=achi2doca_max) + + combination12_code = require_all( + "AM < {am_max}", "ACHI2DOCA(1,2) < {achi2doca_max}").format( + am_max=am_max - 2 * _PION_M, achi2doca_max=achi2doca_max) + bpvdira_min = math.cos(acos_bpvdira_max) + vertex_code = require_all("CHI2VXNDOF < {vchi2pdof_max}", "BPVVALID()", + "BPVVDCHI2() > {bpvvdchi2_min}", + "BPVDIRA() > {bpvdira_min}").format( + vchi2pdof_max=vchi2pdof_max, + bpvvdchi2_min=bpvvdchi2_min, + bpvdira_min=bpvdira_min) + + return N4BodyCombinerWithPVs( + particles=particles, + pvs=pvs, + DecayDescriptors=descriptors, + CombinationCut=combination_code, + MotherCut=vertex_code, + Combination123Cut=combination123_code, + Combination12Cut=combination12_code) + + +@configurable +def make_dstars(dzeros, + soft_pions, + descriptors, + pvs, + max_comb_deltaM=190.0 * MeV, + max_vtx_deltaM=170.0 * MeV, + max_vtx_chi2_pdof=15.0): + + ## TODO: consider rewriting the delta M cuts as cuts on Q so there is + ## no assumed order of the decay products in the decay descriptor. + combination_code = require_all("(AM - AM1) < {max_comb_deltaM}").format( + max_comb_deltaM=max_comb_deltaM) + + vertex_code = require_all( + "(M - M1) < {max_vtx_deltaM}", + "VFASPF(VCHI2/VDOF) < {max_vtx_chi2_pdof}").format( + max_vtx_deltaM=max_vtx_deltaM, max_vtx_chi2_pdof=max_vtx_chi2_pdof) + + return ParticleCombinerWithPVs( + particles=[dzeros, soft_pions], + pvs=pvs, + DecayDescriptors=descriptors, + CombinationCut=combination_code, + MotherCut=vertex_code) + + +def charm_prefilters(): + return [require_gec(), require_pvs(make_pvs())] + + +@configurable +def make_dzero2kpipipi_fordstar(): + pions = make_charm_pions() + kaons = make_charm_kaons() + return make_dzeros( + particles=[pions, kaons], + descriptors=['[D0 -> K- pi- pi+ pi+]cc'], + pvs=make_pvs()) + + +@configurable +def make_dzero2kkkpi_fordstar(): + kaons = make_charm_kaons() + pions = make_charm_pions() + return make_dzeros( + particles=[pions, kaons], + descriptors=['[D0 -> K- K- K+ pi+]cc'], + pvs=make_pvs()) + + +@register_line_builder(all_lines) +@configurable +def dzero2pipipipi_line(name='Hlt2CharmD0ToPimPimPipPipLine', prescale=0.1): + """Line for D0 -> pi- pi- pi+ pi+ + + In principle, can use D0 candidates with selection criteria different from + those in the D* line. + """ + pions = make_charm_pions() + dzeros = make_dzeros( + particles=[pions], + descriptors=['D0 -> pi- pi- pi+ pi+'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dzeros], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dzero2kpipipi_line(name='Hlt2CharmD0ToKmPimPipPipLine', prescale=0.1): + """Line for D0 -> K- pi- pi+ pi+ + C.C. + + In principle, can use D0 candidates with selection criteria different from + those in the D* lines. + """ + pions = make_charm_pions() + kaons = make_charm_kaons() + dzeros = make_dzeros( + particles=[pions, kaons], + descriptors=['[D0 -> K- pi- pi+ pi+]cc'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dzeros], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dzero2kkpipi_line(name='Hlt2CharmD0ToKmKpPimPipLine', prescale=0.1): + """Line for D0 -> K- K+ pi- pi+ + + In principle, can use D0 candidates with selection criteria different from + those in the D* line. + """ + pions = make_charm_pions() + kaons = make_charm_kaons() + dzeros = make_dzeros( + particles=[pions, kaons], + descriptors=['D0 -> K- K+ pi- pi+'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dzeros], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dzero2kkkpi_line(name='Hlt2CharmD0ToKmKmKpPipLine', prescale=0.1): + """Line for D0 -> K- K- K+ pi+ + C.C. + + In principle, can use D0 candidates with selection criteria different from + those in the D* lines. + """ + pions = make_charm_pions() + kaons = make_charm_kaons() + dzeros = make_dzeros( + particles=[pions, kaons], + descriptors=['[D0 -> K- K- K+ pi+]cc'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dzeros], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dstar2dzeropi_dzero2pipipipi_line( + name='Hlt2CharmDstpToD0Pip_D0ToPimPimPipPipLine', prescale=1): + """Line for D*+ -> D0(-> pi- pi- pi+ pi+) pi+ + C.C. + + In order to avoid duplication of combinatorics of the charge-symmetric + final state of the D0, only combinations labelled D0 are made. This + leads to the second 'unphysical' decay descriptor for the D*-. + See https://gitlab.cern.ch/lhcb/Moore/issues/64. + """ + pions = make_charm_pions() + dzeros = make_dzeros( + particles=[pions], + descriptors=['D0 -> pi- pi- pi+ pi+'], + pvs=make_pvs()) + soft_pions = make_has_rich_pions() + dstars = make_dstars( + dzeros=dzeros, + soft_pions=soft_pions, + descriptors=['D*(2010)+ -> D0 pi+', 'D*(2010)- -> D0 pi-'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dstars], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dstar2dzeropi_dzero2kpipipiRS_line( + name='Hlt2CharmDstpToD0Pip_D0ToKmPimPipPipLine', prescale=1): + """Line for D*+ -> D0(-> K- pi- pi+ pi+) pi+ + C.C. + """ + dzeros = make_dzero2kpipipi_fordstar() + soft_pions = make_has_rich_pions() + dstars = make_dstars( + dzeros=dzeros, + soft_pions=soft_pions, + descriptors=['[D*(2010)+ -> D0 pi+]cc'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dstars], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dstar2dzeropi_dzero2kkpipi_line( + name='Hlt2CharmDstpToD0Pip_D0ToKmKpPimPipLine', prescale=1): + """Line for D*+ -> D0(-> K- K+ pi- pi+) pi+ + C.C. + + In order to avoid duplication of combinatorics of the charge-symmetric + final state of the D0, only combinations labelled D0 are made. This + leads to the second 'unphysical' decay descriptor for the D*-. + See https://gitlab.cern.ch/lhcb/Moore/issues/64. + """ + ## TODO: Update D* descriptors if the problem of duplication is solved. + kaons = make_charm_kaons() + pions = make_charm_pions() + dzeros = make_dzeros( + particles=[pions, kaons], + descriptors=['D0 -> K- K+ pi- pi+'], + pvs=make_pvs()) + soft_pions = make_has_rich_pions() + dstars = make_dstars( + dzeros=dzeros, + soft_pions=soft_pions, + descriptors=['D*(2010)+ -> D0 pi+', 'D*(2010)- -> D0 pi-'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dstars], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dstar2dzeropi_dzero2kkkpiRS_line( + name='Hlt2CharmDstpToD0Pip_D0ToKmKmKpPipLine', prescale=1): + """Line for D*+ -> D0(-> K- K- K+ pi+) pi+ + C.C. + """ + dzeros = make_dzero2kkkpi_fordstar() + soft_pions = make_has_rich_pions() + dstars = make_dstars( + dzeros=dzeros, + soft_pions=soft_pions, + descriptors=['[D*(2010)+ -> D0 pi+]cc'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dstars], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dstar2dzeropi_dzero2kpipipiWS_line( + name='Hlt2CharmDstpToD0Pip_D0ToKpPimPimPipLine', prescale=1): + """Line for D*+ -> D0(-> K+ pi- pi- pi+) pi+ + C.C. + + In order to reuse the D0 combinatorics from + dstar2dzeropi_dzero2KpipipiRS_line, the D* decay descriptors are written + unphysically to treat the combinations that are labelled D0 as if they were + D~0. + """ + ## TODO: Update D* descriptors if the problem of duplication is solved. + dzeros = make_dzero2kpipipi_fordstar() + soft_pions = make_has_rich_pions() + dstars = make_dstars( + dzeros=dzeros, + soft_pions=soft_pions, + descriptors=['[D*(2010)- -> D0 pi-]cc'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dstars], + prescale=prescale, + ) + + +@register_line_builder(all_lines) +@configurable +def dstar2dzeropi_dzero2kkkpiWS_line( + name='Hlt2CharmDstpToD0Pip_D0ToKmKpKpPimLine', prescale=1): + """Line for D*+ -> D0(-> K- K+ K+ pi-) pi+ + C.C. + + In order to reuse the D0 combinatorics from + dstar2dzeropi_dzero2KKKpiRS_line, the D* decay descriptors are written + unphysically to treat the combinations that are labelled D0 as if they were + D~0. + """ + ## TODO: Update D* descriptors if the problem of duplication is solved. + dzeros = make_dzero2kkkpi_fordstar() + soft_pions = make_has_rich_pions() + dstars = make_dstars( + dzeros=dzeros, + soft_pions=soft_pions, + descriptors=['[D*(2010)- -> D0 pi-]cc'], + pvs=make_pvs()) + return HltLine( + name=name, + algs=upfront_reconstruction() + charm_prefilters() + [dstars], + prescale=prescale, + ) diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/__init__.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/__init__.py index 487c0818e8f7876bd05a2d4aac5fe31680d0a18c..a37a39196bc007e6a02d6ba76fe63796531d77c8 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/__init__.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/__init__.py @@ -18,6 +18,7 @@ from Moore.config import add_line_to_registry from . import ( Bs2JpsiPhi, D02HH, + D02HHHH, ) __all__ = [ @@ -43,6 +44,7 @@ def _get_all_lines(modules): modules = [ Bs2JpsiPhi, D02HH, + D02HHHH, ] all_lines = _get_all_lines(modules)