diff --git a/B2munuee/B2Vub_MCoptions.py b/B2munuee/B2Vub_MCoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..e5046506ae94f9966cbcf30228d0cb41291167da --- /dev/null +++ b/B2munuee/B2Vub_MCoptions.py @@ -0,0 +1,12 @@ +from B2munuee import my_selections as selections + +# B+ -> mu nu Xu0 (-> X gamma), resonant +# B+ -> mu nu Xu0 (-> X gamma), non-resonant +# B0 -> mu nu Xu (-> X gamma), resonant +# B0 -> mu nu Xu (-> X gamma), non-resonant + +trees = ['default','SameSign','pions','eta'] +isMC=True +addJpsiConstraints=False +run_stripping = False +selections.MySelection(trees, isMC, run_stripping, addJpsiConstraints) \ No newline at end of file diff --git a/B2munuee/Bc2munucc_MCoptions.py b/B2munuee/Bc2munucc_MCoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..110e2a6c13a60da0446cddabf717a490a1515ca9 --- /dev/null +++ b/B2munuee/Bc2munucc_MCoptions.py @@ -0,0 +1,11 @@ +from B2munuee import my_selections as selections + +# Bc -> mu nu chi_c (-> Jpsi gamma) +# Bc -> mu nu Psi2S (-> Jpsi X) +# Bc -> mu nu Psi2S (-> ee) + +trees = ['default', 'SameSign'] +isMC = True +addJpsiConstraints = True +run_stripping = False +selections.MySelection(trees, isMC, run_stripping, addJpsiConstraints) \ No newline at end of file diff --git a/B2munuee/Bu2JpsiK2ee_MCoptions.py b/B2munuee/Bu2JpsiK2ee_MCoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..b5edcef4254d1b9e6f6ea84346b645636a756e6a --- /dev/null +++ b/B2munuee/Bu2JpsiK2ee_MCoptions.py @@ -0,0 +1,9 @@ +from B2munuee import my_selections as selections + +# B+ -> mu nu eta' (-> e e gamma) + +trees = ['JpsiKee'] +isMC = True +addJpsiConstraints = True +run_stripping = True +selections.MySelection(trees, isMC, run_stripping, addJpsiConstraints) \ No newline at end of file diff --git a/B2munuee/Bu2munuee_MCoptions.py b/B2munuee/Bu2munuee_MCoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..4f12913a23ec51e8640b9f51de4d68daaa64bf11 --- /dev/null +++ b/B2munuee/Bu2munuee_MCoptions.py @@ -0,0 +1,9 @@ +from B2munuee import my_selections as selections + +# B+ -> mu nu eta' (-> e e gamma) + +trees = ['default', 'SameSign'] +isMC = True +addJpsiConstraints = False +run_stripping = False +selections.MySelection(trees, isMC, run_stripping, addJpsiConstraints) \ No newline at end of file diff --git a/B2munuee/StrippingB23MuNu.py b/B2munuee/StrippingB23MuNu.py new file mode 100644 index 0000000000000000000000000000000000000000..858afff4b3f4039fd02d73c481f7c7a5ced69141 --- /dev/null +++ b/B2munuee/StrippingB23MuNu.py @@ -0,0 +1,496 @@ +############################################################################### +# (c) Copyright 2000-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. # +############################################################################### + + +############################################################################### +# Custom stripping selection of B23MuNu for restripping in Analysis Production# +# # +# The physics of the selection is identical to the selection of the published # +# version in S28r2p1, S29r2p2 and S34r0p1 but has no RelInfoTools added. # +# # +############################################################################### + +__author__ = 'P. Owen, T.Mombacher' +__date__ = '11/03/2021' +__version__ = '$Revision: 2.0 $' + +__all__ = ( 'B23MuNuConf', 'default_config' ) + +""" +Stripping selection for B to three muons and a neutrino. +""" + +from GaudiConfUtils.ConfigurableGenerators import CombineParticles, FilterDesktop + +from PhysSelPython.Wrappers import Selection, AutomaticData, MergedSelection +from StrippingConf.StrippingLine import StrippingLine +from StrippingUtils.Utils import LineBuilder + + +################# +# +# Define Cuts here +# +################# + +default_config = { + 'NAME' : 'B23MuNu', + 'WGs' : ['Semileptonic'], + 'BUILDERTYPE' : 'B23MuNuConf', + 'CONFIG' : { + # (dimu) cuts + 'FlightChi2' : 30.0, + 'DIRA' : 0.99, + 'BPT' : 2000.0, + 'VertexCHI2' : 4.0, + 'LOWERMASS' : 0.0, # MeV + 'UPPERMASS' : 7500.0, # MeV + 'CORRM_MIN' : 2500.0, # MeV + 'CORRM_MAX' : 10000.0, # MeV + # Track cuts + 'Track_CHI2nDOF' : 3.0, + 'Track_GhostProb' : 0.35, + + # Muon cuts + 'Muon_MinIPCHI2' : 9.0, + 'Muon_PIDmu' : 0.0, + 'Muon_PIDmuK' : 0.0, + 'Muon_PT' : 0.0, + + # Electron cuts + 'Electron_PIDe' : 2.0, + 'Electron_PIDeK' : 0.0, + 'Electron_MinIPCHI2' : 25.0, + 'Electron_PT' : 200.0, + + # GEC + 'SpdMult' : 900, + + 'MisIDPrescale' : 0.01, + }, + 'STREAMS' : ['Semileptonic'] +} + +defaultName = "B23MuNu" + + +class B23MuNuConf(LineBuilder) : + + __configuration_keys__ = default_config['CONFIG'].keys() + + def __init__(self, name, config) : + + + LineBuilder.__init__(self, name, config) + + self.name = name + + + self.TriMuCut = "(BPVCORRM > %(CORRM_MIN)s *MeV) & " \ + "(BPVCORRM < %(CORRM_MAX)s *MeV) & " \ + "(BPVDIRA > %(DIRA)s) & " \ + "(PT > %(BPT)s) & " \ + "(BPVVDCHI2 > %(FlightChi2)s) & " \ + "(VFASPF(VCHI2/VDOF) < %(VertexCHI2)s) & " \ + "(M > %(LOWERMASS)s) & " \ + "(M < %(UPPERMASS)s) " %config + + self.TrackCuts = "(TRCHI2DOF < %(Track_CHI2nDOF)s) & (TRGHP < %(Track_GhostProb)s)" \ + " & (MIPCHI2DV(PRIMARY) > %(Muon_MinIPCHI2)s) " \ + " & (PT > %(Muon_PT)s)" %config + + self.TrackCutsElectron = "(TRCHI2DOF < %(Track_CHI2nDOF)s) & (TRGHP < %(Track_GhostProb)s)" \ + " & (MIPCHI2DV(PRIMARY) > %(Electron_MinIPCHI2)s) " \ + " & (PT > %(Electron_PT)s)" %config + + + self.MuonCut = self.TrackCuts + "& (PIDmu> %(Muon_PIDmu)s) & " \ + " (PIDmu-PIDK> %(Muon_PIDmuK)s)" %config + + + self.ElectronCut = self.TrackCutsElectron + "& (PIDe> %(Electron_PIDe)s) & " \ + " (PIDe-PIDK> %(Electron_PIDeK)s)" %config + + + self.Muons = self.__Muons__(config) + self.Electrons = self.__Electrons__(config) + self.FakeMuons = self.__FakeMuons__(config) + self.FakeElectrons = self.__FakeElectrons__(config) + self.Jpsi = self.__Jpsi__(config) + self.Jpsiee = self.__Jpsiee__(config) + self.Trimu = self.__Trimu__(config) + self.Trie = self.__Trie__(config) + self.MuMue = self.__MuMue__(config) + self.Muee = self.__Muee__(config) + self.FakeTrimu = self.__FakeTrimu__(config) + self.FakeTrie = self.__FakeTrie__(config) + self.FakeMuMue = self.__FakeMuMue__(config) + self.FakeMuee = self.__FakeMuee__(config) + + self.TriMu_line = StrippingLine( + self.name+"_TriMuLine", + prescale = 1, + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.Trimu] + ) + + self.Trie_line = StrippingLine( + self.name+"_TrieLine", + prescale = 1, + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.Trie] + ) + + self.MuMue_line = StrippingLine( + self.name+"_MuMueLine", + prescale = 1, + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.MuMue] + ) + + self.Muee_line = StrippingLine( + self.name+"_MueeLine", + prescale = 1, + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.Muee] + ) + + self.FakeTriMu_line = StrippingLine( + self.name+"_TriFakeMuLine", + prescale = config['MisIDPrescale'], + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.FakeTrimu] + ) + + self.FakeTrie_line = StrippingLine( + self.name+"_TrieFakeLine", + prescale = config['MisIDPrescale'], + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.FakeTrie] + ) + + self.FakeMuMue_line = StrippingLine( + self.name+"_MuMueFakeLine", + prescale = config['MisIDPrescale'], + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.FakeMuMue] + ) + + self.FakeMuee_line = StrippingLine( + self.name+"_MueeFakeLine", + prescale = config['MisIDPrescale'], + FILTER = { + 'Code' : " ( recSummary(LHCb.RecSummary.nSPDhits,'Raw/Spd/Digits') < %(SpdMult)s )" %config , + 'Preambulo' : [ + "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb" + ] + }, + algos=[self.FakeMuee] + ) + + + self.registerLine( self.TriMu_line ) + self.registerLine( self.Trie_line ) + self.registerLine( self.Muee_line ) + self.registerLine( self.MuMue_line ) + self.registerLine( self.FakeTriMu_line ) + self.registerLine( self.FakeTrie_line ) + self.registerLine( self.FakeMuMue_line ) + self.registerLine( self.FakeMuee_line ) + + + + def __Muons__(self, conf): + """ + Filter muons from StdAllLooseMuons + """ + from StandardParticles import StdAllLooseMuons + _muons = StdAllLooseMuons + _filter = FilterDesktop(Code = self.MuonCut) + _sel = Selection("Selection_"+self.name+"_Muons", + RequiredSelections = [ _muons ] , + Algorithm = _filter) + return _sel + + def __Electrons__(self, conf): + """ + Filter Electrons from StdLooseElectrons + """ + from StandardParticles import StdAllLooseElectrons + _electrons = StdAllLooseElectrons + _filter = FilterDesktop(Code = self.ElectronCut) + _sel = Selection("Selection_"+self.name+"_Electrons", + RequiredSelections = [ _electrons ] , + Algorithm = _filter) + return _sel + + + def __FakeMuons__(self, conf): + """ + Filter muons from StdAllNoPIDsMuons + """ + from StandardParticles import StdAllNoPIDsMuons + _fakemuons = StdAllNoPIDsMuons + _filter = FilterDesktop(Code = self.TrackCuts) + _sel = Selection("Selection_"+self.name+"_FakeMuons", + RequiredSelections = [ _fakemuons ] , + Algorithm = _filter) + return _sel + + def __FakeElectrons__(self, conf): + """ + Filter electrons from StdAllNoPIDsElectrons + """ + from StandardParticles import StdAllNoPIDsElectrons + _fakeelectrons = StdAllNoPIDsElectrons + _filter = FilterDesktop(Code = self.TrackCutsElectron) + _sel = Selection("Selection_"+self.name+"_FakeElectrons", + RequiredSelections = [ _fakeelectrons ] , + Algorithm = _filter) + return _sel + + + def __Jpsi__(self, conf): + """ + Creates Jpsi as proxy for dimuon + """ + from PhysSelPython.Wrappers import AutomaticData, Selection, SelectionSequence, DataOnDemand, MergedSelection + from GaudiConfUtils.ConfigurableGenerators import CombineParticles + #from Configurables import CombineParticles + + CombineJpsi= CombineParticles(DecayDescriptors = ["J/psi(1S) -> mu+ mu-","[J/psi(1S) -> mu+ mu+]cc"], + MotherCut = "ALL") + + sel_name = "Jpsi" + + from PhysSelPython.Wrappers import Selection + SelJpsi = Selection("Sel_" + self.name + "_Jpsi", Algorithm = CombineJpsi, + RequiredSelections = [ self.Muons ] ) + return SelJpsi + + def __Jpsiee__(self, conf): + """ + Creates Jpsi as proxy for dielectron + """ + from PhysSelPython.Wrappers import AutomaticData, Selection, SelectionSequence, DataOnDemand, MergedSelection + from GaudiConfUtils.ConfigurableGenerators import CombineParticles + #from Configurables import CombineParticles + + CombineJpsi= CombineParticles(DecayDescriptors = ["J/psi(1S) -> e+ e-","[J/psi(1S) -> e+ e+]cc"], + MotherCut = "ALL") + + sel_name = "Jpsi" + + from PhysSelPython.Wrappers import Selection + SelJpsi = Selection("Sel_" + self.name + "_Jpsiee", Algorithm = CombineJpsi, + RequiredSelections = [ self.Electrons ] ) + return SelJpsi + + + + def __Trimu__(self, conf): + ''' + Create trimuon + ''' + from GaudiConfUtils.ConfigurableGenerators import CombineParticles + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptors = ["[B+ -> mu+ mu+ mu-]cc", "[B+ -> mu+ mu+ mu+]cc"] + sel_name="TriMu" + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.Muons ] ) + return SelTriMuon + + def __Trie__(self, conf): + ''' + Create trielectron + ''' + from GaudiConfUtils.ConfigurableGenerators import CombineParticles + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptors = ["[B+ -> e+ e+ e-]cc", "[B+ -> e+ e+ e+]cc"] + sel_name="Trie" + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.Electrons ] ) + return SelTriMuon + + + def __MuMue__(self, conf): + ''' + Create MuMue combination + ''' + from GaudiConfUtils.ConfigurableGenerators import CombineParticles + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptors = ["[B+ -> e+ mu+ mu-]cc","[B+ -> mu+ mu+ e-]cc", "[B+ -> mu+ mu+ e+]cc"] + sel_name="MuMue" + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.Muons, self.Electrons ] ) + return SelTriMuon + + + def __Muee__(self, conf): + ''' + Create Muee combination + ''' + from GaudiConfUtils.ConfigurableGenerators import CombineParticles + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptors = ["[B+ -> mu+ e+ e-]cc", "[B+ -> e+ e+ mu-]cc", "[B+ -> mu+ e+ e+]cc"] + sel_name="Muee" + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.Muons, self.Electrons ] ) + return SelTriMuon + + + def __FakeTrimu__(self, conf): + """ + Create fake trimuon + """ + + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptor = "[B+ -> J/psi(1S) mu+]cc" + sel_name="FakeTriMu" + #CombineTriMuon.CombinationCut = self.TriMuLowQ2CombCut + #["[B+ ->mu+ mu+ mu-]cc", "[B+ ->mu+ mu+ mu+]cc"] + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + #SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, Algorithm = CombineTriMuon, + # RequiredSelections = [self.Jpsi, self.FakeMuon ]) + SelTriMuon = Selection(sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.FakeMuons, self.Jpsi ]) + + + return SelTriMuon + + def __FakeTrie__(self, conf): + """ + Create fake trielectron + """ + + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptor = "[B+ -> J/psi(1S) e+]cc" + sel_name="FakeTrie" + #CombineTriMuon.CombinationCut = self.TriMuLowQ2CombCut + #["[B+ ->mu+ mu+ mu-]cc", "[B+ ->mu+ mu+ mu+]cc"] + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + #SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, Algorithm = CombineTriMuon, + # RequiredSelections = [self.Jpsi, self.FakeMuon ]) + SelTriMuon = Selection(sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.FakeElectrons, self.Jpsiee ]) + + + return SelTriMuon + + + def __FakeMuMue__(self, conf): + """ + Create fake MuMue + """ + + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptor = "[B+ -> J/psi(1S) e+]cc" + sel_name="FakeMuMue" + #CombineTriMuon.CombinationCut = self.TriMuLowQ2CombCut + #["[B+ ->mu+ mu+ mu-]cc", "[B+ ->mu+ mu+ mu+]cc"] + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + #SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, Algorithm = CombineTriMuon, + # RequiredSelections = [self.Jpsi, self.FakeMuon ]) + SelTriMuon = Selection(sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.FakeElectrons, self.Jpsi ]) + + + return SelTriMuon + + def __FakeMuee__(self, conf): + """ + Create fake Muee + """ + + CombineTriMuon = CombineParticles() + CombineTriMuon.DecayDescriptor = "[B+ -> J/psi(1S) mu+]cc" + sel_name="FakeMuee" + #CombineTriMuon.CombinationCut = self.TriMuLowQ2CombCut + #["[B+ ->mu+ mu+ mu-]cc", "[B+ ->mu+ mu+ mu+]cc"] + CombineTriMuon.MotherCut = self.TriMuCut + # choose + + from PhysSelPython.Wrappers import Selection + #SelTriMuon = Selection("Sel_" + self.name + "_"+sel_name, Algorithm = CombineTriMuon, + # RequiredSelections = [self.Jpsi, self.FakeMuon ]) + SelTriMuon = Selection(sel_name, + Algorithm = CombineTriMuon, + RequiredSelections = [ self.FakeMuons, self.Jpsiee ]) + + + return SelTriMuon diff --git a/B2munuee/__init__.py b/B2munuee/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/B2munuee/info.yaml b/B2munuee/info.yaml new file mode 100644 index 0000000000000000000000000000000000000000..80e4ed68c1529e53128e68967b71ab14b3956e15 --- /dev/null +++ b/B2munuee/info.yaml @@ -0,0 +1,119 @@ +defaults: + application: DaVinci/v46r7 + wg: SL + automatically_configure: yes + turbo: no + inform: + - fabian.christoph.glaser@cern.ch + +{%- set mc_datasets = [ + ('2016', 'Sim09m', 'Trig0x6139160F', '16', '03a', '28r2p1'), + ('2017', 'Sim09m', 'Trig0x62661709', '17', '04a-WithTurcal', '29r2p2'), + ('2018', 'Sim09m', 'Trig0x617d18a4', '18', '05-WithTurcal', '34r0p2'), +]%} + +{%- for year, sim, trig, reco, turbo, strip in mc_datasets %} + {%- for polarity in ['MagDown','MagUp'] %} + +{{year}}_{{polarity}}_MC_Bu2munuetap2eeg: + #B+ -> mu nu eta' (-> e+ e- gamma) + input: + bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-Pythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/12513200/ALLSTREAMS.DST + output: Bu2munuetap2eeg_MC.ROOT + options: Bu2munuee_MCoptions.py + +{{year}}_{{polarity}}_MC_Bc2munuchic2Jpsi: + #Bc+ -> mu nu chi_c (-> Jpsi X) + input: + bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-BcVegPyPythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/14643230/ALLSTREAMS.DST + output: Bc2munuchic2Jpsi_MC.ROOT + options: Bc2munucc_MCoptions.py + +{{year}}_{{polarity}}_MC_Bc2munuPsi2Jpsi: + #Bc+ -> mu nu psi(2S) (-> Jpsi X) + input: + bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-BcVegPyPythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/14845020/ALLSTREAMS.DST + output: Bc2munuPsi2Jpsi_MC.ROOT + options: Bc2munucc_MCoptions.py + +{{year}}_{{polarity}}_MC_Bc2munuPsi2ee: + #Bc+ -> mu nu psi(2S) + input: + bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-BcVegPyPythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/14543050/ALLSTREAMS.DST + output: Bc2munuPsi2ee_MC.ROOT + options: Bc2munucc_MCoptions.py + + {%- endfor %} +{%- endfor %} + + +###### +# MC samples generated using SplitSim for photon conversion +###### + +{%- set conversion_datasets = [ + ('2016', 'Sim09m-SplitSim01', 'Trig0x6139160F', '16', '03a', '28r2p1'), + ('2017', 'Sim09m-SplitSim01', 'Trig0x62661709', '17', '04a-WithTurcal', '29r2p2'), + ('2018', 'Sim09m-SplitSim01', 'Trig0x617d18a4', '18', '05-WithTurcal', '34r0p2'), +]%} + +{%- for year, sim, trig, reco, turbo, strip in conversion_datasets %} + {%- for polarity in ['MagDown','MagUp'] %} + + #B+ -> mu nu Xu resonant + #input: + # bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-Pythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/12711000/ALLSTREAMS.DST + #output: Bu2munuXu_MC.ROOT + #options: B2Vub_MCoptions.py + + #B0 -> mu nu Xu resonant + #input: + # bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-Pythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/11512400/ALLSTREAMS.DST + #output: Bd2munuXu_MC.ROOT + #options: B2Vub_MCoptions.py + +{{year}}_{{polarity}}_MC_Bu2munuVub: + #B+ -> mu nu Xu non-resonant + input: + bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-Pythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/12511005/ALLSTREAMS.DST + output: Bu2munuVub_MC.ROOT + options: B2Vub_MCoptions.py + +{{year}}_{{polarity}}_MC_Bd2munuVub: + #B0 -> mu nu Xu non-resonant + input: + bk_query: /MC/{{year}}/Beam6500GeV-{{year}}-{{polarity}}-Nu1.6-25ns-Pythia8/{{sim}}/{{trig}}/Reco{{reco}}/Turbo{{turbo}}/Stripping{{strip}}NoPrescalingFlagged/11511003/ALLSTREAMS.DST + output: Bd2munuVub_MC.ROOT + options: B2Vub_MCoptions.py + + {%- endfor %} +{%- endfor %} + +###### +# MC samples of B+ -> Jpsi K reference channel +###### + +{%- for polarity in ['MagDown','MagUp'] %} + +2016_{{polarity}}_MC_Bu2JpsiK2ee: + #B+ -> Jpsi (-> e+ e-) K+ + input: + bk_query: /MC/2016/Beam6500GeV-2016-{{polarity}}-Nu1.6-25ns-Pythia8/Sim09g/Trig0x6139160F/Reco16/Turbo03/Stripping28r1NoPrescalingFlagged/12153001/ALLSTREAMS.DST + output: Bu2JpsiK2ee_MC.ROOT + options: Bu2JpsiK2ee_MCoptions.py + +2017_{{polarity}}_MC_Bu2JpsiK2ee: + #B+ -> Jpsi (-> e+ e-) K+ + input: + bk_query: /MC/2017/Beam6500GeV-2017-{{polarity}}-Nu1.6-25ns-Pythia8/Sim09i/Trig0x62661709/Reco17/Turbo04a-WithTurcal/Stripping29r2NoPrescalingFlagged/12153001/ALLSTREAMS.DST + output: Bu2JpsiK2ee_MC.ROOT + options: Bu2JpsiK2ee_MCoptions.py + +2018_{{polarity}}_MC_Bu2JpsiK2ee: + #B+ -> Jpsi (-> e+ e-) K+ + input: + bk_query: /MC/2018/Beam6500GeV-2018-{{polarity}}-Nu1.6-25ns-Pythia8/Sim09i/Trig0x617d18a4/Reco18/Turbo05-WithTurcal/Stripping34NoPrescalingFlagged/12153001/ALLSTREAMS.DST + output: Bu2JpsiK2ee_MC.ROOT + options: Bu2JpsiK2ee_MCoptions.py + +{%- endfor %} \ No newline at end of file diff --git a/B2munuee/my_selections.py b/B2munuee/my_selections.py new file mode 100644 index 0000000000000000000000000000000000000000..70213828ef9fa22649165b4520f0053d1c9a626f --- /dev/null +++ b/B2munuee/my_selections.py @@ -0,0 +1,366 @@ +from Configurables import DaVinci, DecayTreeTuple, GaudiSequencer +from PhysSelPython.Wrappers import SelectionSequence +from DecayTreeTuple.Configuration import * + +from Configurables import FilterInTrees, CombineParticles, DiElectronMaker +from PhysSelPython.Wrappers import Selection, AutomaticData + +from CommonParticles.Utils import updateDoD + +from GaudiKernel.SystemOfUnits import MeV +from B2munuee import my_utils as utils + +from StrippingConf.Configuration import StrippingConf, StrippingStream +from Configurables import EventNodeKiller, ProcStatusCheck + +from B2munuee import StrippingB23MuNu + +cuts = { + # B mother cuts + "CORRM_MIN": 2500.0 * MeV, + "CORRM_MAX": 10000.0 * MeV, + "DIRA": 0.99, + "BPT": 2000.0 * MeV, + "FlightChi2": 30.0, + "VertexChi2": 4.0, + "LOWERMASS": 0.0 * MeV, + "UPPERMASS": 7500.0 * MeV, + + # cuts on electron + "Electron_PT": 200.0 * MeV, + + # cuts on di-electron + "DiElectron_PT": 0.0 * MeV, + "DiElectron_MinMass": 0.0 * MeV, + "DiElectron_MaxMass": 6300.0 * MeV, + + # cuts for photon selection + "Pi0_gamma_PT": 250.0 * MeV, + "Pi0_Jpsi_M_lower": 90.0 * MeV, + "Pi0_Jpsi_M_upper": 210.0 * MeV, + + "Eta_gamma_PT": 400.0 * MeV, + "Eta_Jpsi_M_lower": 450.0 * MeV, + "Eta_Jpsi_M_upper": 650.0 * MeV, +} + +def RunB23MuNuStripping(B23MuNu_lines): + ''' + Run selected lines of the B23MuNu stripping on MC + Physics of the stripping selection is identical to the + published versions of S28r2p1, S29r2p2 and S34r0p1 + but has no RelInfoTool added due to missing muon raw coordinates + ''' + STRIPPING_LINES = ['Stripping'+l for l in B23MuNu_lines] + + STRIPPING_VERSIONS = { + '2016': 'Stripping28r2p1', + '2017': 'Stripping29r2p2', + '2018': 'Stripping34r0p1' + } + event_node_killer = EventNodeKiller('StripKiller') + event_node_killer.Nodes = ['/Event/AllStreams', '/Event/Strip'] + + # get year of MC + data_type = DaVinci().DataType + + stripping_version = STRIPPING_VERSIONS[data_type] + + # declare custom stream + custom_stream = StrippingStream('AllStreams') + + my_linebuilder = StrippingB23MuNu.B23MuNuConf( + 'B23MuNu', StrippingB23MuNu.default_config['CONFIG'] + ) + + for line in my_linebuilder.lines(): + if line.name() in STRIPPING_LINES: + line._prescale = 1.0 + custom_stream.appendLines([line]) + + filterBadEvents = ProcStatusCheck() + + sc = StrippingConf(Streams=[custom_stream], + MaxCandidates=2000, + AcceptBadEvents=False, + BadEventSelection=filterBadEvents) + + DaVinci().appendToMainSequence([event_node_killer, sc.sequence()]) + +def RunDiElectronMaker(line, oppositeSign=True): + ''' + Run DiElectronMaker on the electrons that passed the stripping selection + This instance does not use the proto-particles + ''' + if oppositeSign: + dieLL = DiElectronMaker('DiElectron_' + line) + else: + dieLL = DiElectronMaker('DiElectron_' + line + '_SS') + + dieLL.Particle = "J/psi(1S)" + dieLL.ElectronInputs = ['Phys/Electrons_'+line+'/Particles'] + + # some of these cuts do not differ from the default values + # but are implemented to allow easy configuration + # cut on electron pt is already applied in the selection for the input container + # is kept nonetheless in case the default requirement of DiElectronMaker + # is tighter than the custom requirement + dieLL.DiElectronMassMax = cuts['DiElectron_MaxMass'] + dieLL.DiElectronMassMin = cuts['DiElectron_MinMass'] + dieLL.DiElectronPtMin = cuts['DiElectron_PT'] + dieLL.ElectronPtMin = cuts['Electron_PT'] + dieLL.AddBrem = True + dieLL.OppositeSign = oppositeSign + + updateDoD(dieLL) + +def MakePion(Dielectron): + ''' + Conbine di-electron with a photon that is compatible with coming from a pi0 decay + ''' + + pionDaughter_cuts = { + 'gamma': ("(PT > {Pi0_gamma_PT})").format(**cuts), + 'J/psi(1S)': ("(MM < {Pi0_Jpsi_M_upper})").format(**cuts) + } + pionCombination_cut = ("(AM > {Pi0_Jpsi_M_lower})" + " & (AM < {Pi0_Jpsi_M_upper})").format(**cuts) + + photons = AutomaticData('Phys/StdLooseAllPhotons/Particles') + + pion = CombineParticles( + "pion_withPhoton", + DecayDescriptor='pi0 -> J/psi(1S) gamma', + DaughtersCuts=pionDaughter_cuts, + CombinationCut=pionCombination_cut, + MotherCut=("(M > {Pi0_Jpsi_M_lower})" + " & (M < {Pi0_Jpsi_M_upper})").format(**cuts) + ) + + pion_sel = Selection( + 'pi0_selection', + Algorithm=pion, + RequiredSelections=[ + Dielectron, + photons]) + + return pion_sel + + +def MakeEta(Dielectron): + ''' + Conbine di-electron with a photon that is compatible with coming from a eta decay + ''' + etaDaughter_cuts = { + 'gamma': ("(PT > {Eta_gamma_PT})").format(**cuts), + 'J/psi(1S)': ("(MM < {Eta_Jpsi_M_upper})").format(**cuts) + } + etaCombination_cut = ("(AM > {Eta_Jpsi_M_lower})" + " & (AM < {Eta_Jpsi_M_upper})").format(**cuts) + + photons = AutomaticData('Phys/StdLooseAllPhotons/Particles') + + eta = CombineParticles( + "eta_withPhoton", + DecayDescriptor='eta -> J/psi(1S) gamma', + DaughtersCuts=etaDaughter_cuts, + CombinationCut=etaCombination_cut, + MotherCut=("(M > {Eta_Jpsi_M_lower})" + " & (M < {Eta_Jpsi_M_upper})").format(**cuts) + ) + + eta_sel = Selection( + 'eta_selection', + Algorithm=eta, + RequiredSelections=[ + Dielectron, + photons]) + + return eta_sel + + +def MakeMother(tree): + ''' + Combine di-electon object and muon to a B candidate and apply default cuts + ''' + BMotherCut = ( + "(BPVCORRM > {CORRM_MIN} *MeV) & " + "(BPVCORRM < {CORRM_MAX} *MeV) & " + "(BPVDIRA > {DIRA}) & " + "(PT > {BPT}) & " + "(BPVVDCHI2 > {FlightChi2}) & " + "(VFASPF(VCHI2/VDOF) < {VertexChi2}) &" + "(M > {LOWERMASS}) & " + "(M < {UPPERMASS})").format(**cuts) + + if tree in ['pions']: + muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles') + Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine/Particles') + + pi0 = MakePion(Jpsi) + B = CombineParticles('MakeMother_' + tree, + DecayDescriptor='[B+ -> pi0 mu+]cc', + MotherCut=BMotherCut) + B_sel = Selection('Sel_B_' + tree, Algorithm=B, + RequiredSelections=[pi0, muons]) + elif tree in ['eta']: + muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles') + Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine/Particles') + + eta = MakeEta(Jpsi) + B = CombineParticles('MakeMother_' + tree, + DecayDescriptor='[B+ -> eta mu+]cc', + MotherCut=BMotherCut) + B_sel = Selection('Sel_B_' + tree, Algorithm=B, + RequiredSelections=[eta, muons]) + elif tree in ['SameSign']: + muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles') + Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine_SS/Particles') + + B = CombineParticles('MakeMother_' + tree, + DecayDescriptors=[ + '[B+ -> J/psi(1S) mu+]cc', '[B+ -> J/psi(1S) mu-]cc'], + MotherCut=BMotherCut) + B_sel = Selection('Sel_B_' + tree, Algorithm=B, + RequiredSelections=[Jpsi, muons]) + elif tree in ['default']: + muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles') + Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine/Particles') + + B = CombineParticles('MakeMother_' + tree, + DecayDescriptors=['[B+ -> J/psi(1S) mu+]cc'], + MotherCut=BMotherCut) + B_sel = Selection('Sel_B_' + tree, Algorithm=B, + RequiredSelections=[Jpsi, muons]) + elif tree in ['JpsiKee']: + muons = AutomaticData('Phys/Muons_B23MuNu_MueeFakeLine/Particles') + Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeFakeLine/Particles') + + B = CombineParticles('MakeMother_' + tree, + DecayDescriptors=['[B+ -> J/psi(1S) mu+]cc'], + MotherCut=BMotherCut) + B_sel = Selection('Sel_B_' + tree, Algorithm=B, + RequiredSelections=[Jpsi, muons]) + else: + raise ValueError( + tree + ' is no valid tree for MakeMother') + + return B_sel + + +def MakeNTuples(B, name): + ''' + Make nTuples with di-lepton object + Reference channels follow the naming convention of the respective signal channel + ''' + dtt = DecayTreeTuple('B2MuNuEE_' + name) + if name in ['default','JpsiKee']: + dtt.setDescriptorTemplate( + '${B}[B+ -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${mu}mu+]CC') + elif name in ['SameSign']: + dtt.setDescriptorTemplate( + '${B}[B+ -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e+) ${mu}[mu+]cc]CC') + elif name in ['pions']: + dtt.setDescriptorTemplate( + '${B}[B+ -> ${pi}(pi0 -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${gamma}gamma) ${mu}mu+]CC') + elif name in ['eta']: + dtt.setDescriptorTemplate( + '${B}[B+ -> ${eta}(eta -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${gamma}gamma) ${mu}mu+]CC') + else: + raise ValueError( + name + ' is not a valid option for MakeNTuples(B, name)') + + Seq = SelectionSequence('Sequence_' + name, TopSelection=B) + dtt.Inputs = Seq.outputLocations() + + _seq = GaudiSequencer('MySequencer_' + name) + _seq.Members += [Seq.sequence(), dtt] + DaVinci().appendToMainSequence([_seq]) + + return dtt + +def MySelection(trees, isMC, run_stripping, addJpsiConstraints): + + if isMC and run_stripping: + stream = 'Phys' + elif isMC and not run_stripping: + stream = '/Event/AllStreams/Phys' + else: + stream = '/Event/Semileptonic/Phys' + + # check if trees are valid + valid_trees = ['default', 'SameSign', 'pions', 'eta', 'JpsiKee'] + if not all(item in valid_trees for item in trees): + raise ValueError('At least one requested tree is not supported') + + # run restripping if required + if run_stripping is True: + lines = [] + if any(item in ['default','pions','eta','SameSign'] for item in trees): + lines.append('B23MuNu_MueeLine') + if 'JpsiKee' in trees: + lines.append('B23MuNu_MueeFakeLine') + RunB23MuNuStripping(lines) + + # make filter for MueeLine if used in any tree + if any(item in ['default','pions','eta','SameSign'] for item in trees): + # select muons from default MueeLine + muon_Muee = FilterInTrees('Muons_B23MuNu_MueeLine', + Inputs=['{}/B23MuNu_MueeLine/Particles'.format(stream)], + Code="('mu+' == ABSID)") + updateDoD(muon_Muee) + + # select electrons from default MueeLine + electrons_Muee = FilterInTrees('Electrons_B23MuNu_MueeLine', + Inputs=['{}/B23MuNu_MueeLine/Particles'.format(stream)], + Code="('e+' == ABSID)") + updateDoD(electrons_Muee) + + # run DiElectronMaker for electrons from MueeLine if used in any tree + if any(item in ['default','pions','eta'] for item in trees): + RunDiElectronMaker('B23MuNu_MueeLine') + + + for tree in trees: + # if tree does not use MueeLine + if tree in ['JpsiKee']: + line = 'B23MuNu_MueeFakeLine' + + # select muons from stripping or std container + muon_filter = FilterInTrees('Muons_'+line, + Inputs=['{}/{}/Particles'.format(stream, line)], + Code="('mu+' == ABSID)") + updateDoD(muon_filter) + + # select electrons from stripping or std container + electrons_filter = FilterInTrees('Electrons_'+line, + Inputs=['{}/{}/Particles'.format(stream, line)], + Code="('e+' == ABSID)") + updateDoD(electrons_filter) + + # run dieelctron maker + RunDiElectronMaker('B23MuNu_MueeFakeLine') + + # if SameSign also need to run DiElectronMaker again + if tree == 'SameSign': + RunDiElectronMaker('B23MuNu_MueeLine', oppositeSign=False) + + # make Mother particle + mother = MakeMother(tree) + + # make nTuple + dtt = MakeNTuples(mother, tree) + + # now fill the tuples + utils.DefaultToolList(dtt) + if isMC: + utils.MCToolList(dtt) + utils.TriggerToolList(dtt) + utils.DefaultLoKi(dtt) + if tree in ['pions','eta']: + utils.AddProtoPInfo(dtt) + utils.AddDOCA(dtt) + if tree in ['default'] and addJpsiConstraints: + utils.JpsiConstraints(dtt) + utils.TupleToolConeIsolation(dtt) + utils.TupleToolSubstitution(dtt) \ No newline at end of file diff --git a/B2munuee/my_utils.py b/B2munuee/my_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..dc2c402519ec3efbf23751083323ce886c406033 --- /dev/null +++ b/B2munuee/my_utils.py @@ -0,0 +1,300 @@ +from Configurables import TupleToolTISTOS, BackgroundCategory +from Configurables import LoKi__Hybrid__TupleTool, LoKi__Hybrid__Dict2Tuple, LoKi__Hybrid__DTFDict, LoKi__Hybrid__DictOfFunctors + + +def DefaultToolList(dtt): + '''Add default list of TupleTools''' + dtt.ToolList = [ + # default tools + "TupleToolKinematic", + "TupleToolEventInfo", + "TupleToolGeometry", + "TupleToolANNPID", + + # additional tools w/o configuration required + "TupleToolRecoStats", + "TupleToolPhotonInfo", + "TupleToolPropertime", + 'TupleToolCorrectedMass'] + + # add PID info + PID = dtt.addTupleTool("TupleToolPid") + PID.Verbose = True + + # add VELO track info + TrackInfo = dtt.addTupleTool('TupleToolTrackInfo') + TrackInfo.Verbose = True + + # add Brem info for single and di-eletcron + BremInfo = dtt.addTupleTool('TupleToolBremInfo') + BremInfo.fullDST = True + BremInfo.Particle = ['e+', 'J/psi(1S)'] + + # add tool for isolation + IsoGeneric = dtt.addTupleTool("TupleToolIsoGeneric") + IsoGeneric.Verbose = True + + dtt.ep.addTupleTool('TupleToolL0Calo/ep_L0Calo') + dtt.ep.ep_L0Calo.WhichCalo = "ECAL" + dtt.em.addTupleTool('TupleToolL0Calo/em_L0Calo') + dtt.em.em_L0Calo.WhichCalo = "ECAL" + +def MCToolList(dtt): + backgroundinfo = dtt.addTupleTool("TupleToolMCBackgroundInfo") + backgroundinfo.addTool(BackgroundCategory('BackgroundCategory')) + + MCTruth = dtt.addTupleTool("TupleToolMCTruth") + MCTruth.addTupleTool("MCTupleToolHierarchy") + +def TriggerToolList(dtt): + '''Add trigger information''' + + L0trigger = ["L0ElectronDecision", + "L0MuonDecision", + "L0HadronDecision", + "L0PhotonDecision"] + + Hlt1trigger = ["Hlt1TrackMVADecision", + "Hlt1TwoTrackMVADecision", + "Hlt1TrackMVALooseDecision", + "Hlt1TwoTrackMVALooseDecision", + "Hlt1TrackMuonDecision", + "Hlt1TrackMuonMVADecision", + "Hlt1L0AnyDecision"] + + Hlt2trigger = [ + "Hlt2SingleMuonDecision", + # Topo + "Hlt2Topo2BodyDecision", + "Hlt2Topo3BodyDecision", + "Hlt2TopoE2BodyDecision", + "Hlt2TopoE3BodyDecision", + "Hlt2TopoEE2BodyDecision", + "Hlt2TopoEE3BodyDecision", + "Hlt2TopoMu2BodyDecision", + "Hlt2TopoMu3BodyDecision", + "Hlt2TopoMuE2BodyDecision", + "Hlt2TopoMuE3BodyDecision", + "Hlt2TopoMu2BodyBBDTDecision", + "Hlt2TopoMu3BodyBBDTDecision"] + + triggerList = L0trigger + Hlt1trigger + Hlt2trigger + + TISTOS = TupleToolTISTOS('TISTOS') + TISTOS.VerboseL0 = True + TISTOS.VerboseHlt1 = True + TISTOS.VerboseHlt2 = True + TISTOS.TriggerList = triggerList + dtt.B.addTupleTool(TISTOS) + + # for electrons and muon only need L0 information + TISTOS_Leptons = TupleToolTISTOS('TISTOS_Leptons') + TISTOS_Leptons.VerboseL0 = True + TISTOS_Leptons.VerboseHlt1 = False + TISTOS_Leptons.VerboseHlt2 = False + TISTOS_Leptons.TriggerList = L0trigger + dtt.ep.addTupleTool(TISTOS_Leptons) + dtt.em.addTupleTool(TISTOS_Leptons) + dtt.mu.addTupleTool(TISTOS_Leptons) + + +def DefaultLoKi(dtt): + ''' + Add default LoKi functors for decay topology and kinematics + ''' + + LoKi_All = dtt.addTupleTool("LoKi::Hybrid::TupleTool/LoKi_All") + LoKi_All.Variables = { + 'MINIPCHI2': "MIPCHI2DV(PRIMARY)", + 'MINIP': "MIPDV(PRIMARY)", + 'ETA': 'ETA', + 'PHI': 'PHI' + } + + # add track information + LoKi_Track = LoKi__Hybrid__TupleTool("LoKi_Track") + LoKi_Track.Preambulo = ["from LoKiTracks.decorators import *"] + LoKi_Track.Variables = { + "TrPX": "TRFUN(TrPX)", + "TrPY": "TRFUN(TrPY)", + "TrPZ": "TRFUN(TrPZ)", + "TrP": "TRFUN(TrP)", + } + + dtt.ep.ToolList += ["LoKi::Hybrid::TupleTool/LoKi_Track"] + dtt.ep.addTool(LoKi_Track) + dtt.em.ToolList += ["LoKi::Hybrid::TupleTool/LoKi_Track"] + dtt.em.addTool(LoKi_Track) + + LoKi_B = dtt.B.addTupleTool("LoKi::Hybrid::TupleTool/LoKi_B") + LoKi_B.Variables = { + 'DIRA_OWNPV': "BPVDIRA", + 'ENDVERTEX_CHI2': "VFASPF(VCHI2/VDOF)", + 'X_travelled': "VFASPF(VX)-BPV(VX)", + 'Y_travelled': "VFASPF(VY)-BPV(VY)", + 'Z_travelled': "VFASPF(VZ)-BPV(VZ)", + 'P_Parallel': "BPVDIRA*P", + 'P_Perp': "sin(acos(BPVDIRA))*P", + 'BPVVDZ': "BPVVDZ", + } + +def AddProtoPInfo(dtt): + + LokiVars = { + "PP_CaloNeutralID": "PPINFO(LHCb.ProtoParticle.CaloNeutralID, -1000)", + "PP_CaloChargedID": "PPINFO(LHCb.ProtoParticle.CaloChargedID, -1000)", + "PP_CaloDepositID": "PPINFO(LHCb.ProtoParticle.CaloDepositID, -1000)", + "PP_CaloBremMatch": "PPINFO(LHCb.ProtoParticle.CaloBremMatch, -1000)", + "PP_CaloBremChi2": "PPINFO(LHCb.ProtoParticle.CaloBremChi2, -1000)", + } + + LoKi_ep = dtt.ep.addTupleTool("LoKi::Hybrid::TupleTool") + LoKi_ep.Variables = LokiVars + + LoKi_em = dtt.em.addTupleTool("LoKi::Hybrid::TupleTool") + LoKi_em.Variables = LokiVars + + if dtt.Decay in ['[B+ -> ^(pi0 -> ^(J/psi(1S) -> ^e+ ^e-) ^gamma) ^mu+]CC', + '[B+ -> ^(eta -> ^(J/psi(1S) -> ^e+ ^e-) ^gamma) ^mu+]CC']: + + LoKi_gamma = dtt.gamma.addTupleTool("LoKi::Hybrid::TupleTool") + LoKi_gamma.Variables = LokiVars + +def AddDOCA(dtt): + ''' + Add distance of closest approach DOCA for the three lepton tracks + ''' + if dtt.Decay == '[B+ -> ^(J/psi(1S) -> ^e+ ^e-) ^mu+]CC': + doca_name, location1, location2 = zip( + ['epem', + '[B+ -> (J/psi(1S) -> ^e+ e-) mu+]CC', + '[B+ -> (J/psi(1S) -> e+ ^e-) mu+]CC'], + ['epmu', + '[B+ -> (J/psi(1S) -> ^e+ e-) mu+]CC', + '[B+ -> (J/psi(1S) -> e+ e-) ^mu+]CC'], + ['emmu', + '[B+ -> (J/psi(1S) -> e+ ^e-) mu+]CC', + '[B+ -> (J/psi(1S) -> e+ e-) ^mu+]CC']) + elif dtt.Decay == '[B+ -> ^(J/psi(1S) -> ^e+ ^e+) ^[mu+]cc]CC': + doca_name, location1, location2 = zip( + ['epem', + '[B+ -> (J/psi(1S) -> ^e+ e+) [mu+]cc]CC', + '[B+ -> (J/psi(1S) -> e+ ^e+) [mu+]cc]CC'], + ['epmu', + '[B+ -> (J/psi(1S) -> ^e+ e+) [mu+]cc]CC', + '[B+ -> (J/psi(1S) -> e+ e+) ^[mu+]cc]CC'], + ['emmu', + '[B+ -> (J/psi(1S) -> e+ ^e+) [mu+]cc]CC', + '[B+ -> (J/psi(1S) -> e+ e+) ^[mu+]cc]CC']) + elif dtt.Decay == '[B+ -> ^(pi0 -> ^(J/psi(1S) -> ^e+ ^e-) ^gamma) ^mu+]CC': + doca_name, location1, location2 = zip( + ['epem', + '[B+ -> (pi0 -> (J/psi(1S) -> ^e+ e-) gamma) mu+]CC', + '[B+ -> (pi0 -> (J/psi(1S) -> e+ ^e-) gamma) mu+]CC'], + ['epmu', + '[B+ -> (pi0 -> (J/psi(1S) -> ^e+ e-) gamma) mu+]CC', + '[B+ -> (pi0 -> (J/psi(1S) -> e+ e-) gamma) ^mu+]CC'], + ['emmu', + '[B+ -> (pi0 -> (J/psi(1S) -> e+ ^e-) gamma) mu+]CC', + '[B+ -> (pi0 -> (J/psi(1S) -> e+ e-) gamma) ^mu+]CC']) + elif dtt.Decay == '[B+ -> ^(eta -> ^(J/psi(1S) -> ^e+ ^e-) ^gamma) ^mu+]CC': + doca_name, location1, location2 = zip( + ['epem', + '[B+ -> (eta -> (J/psi(1S) -> ^e+ e-) gamma) mu+]CC', + '[B+ -> (eta -> (J/psi(1S) -> e+ ^e-) gamma) mu+]CC'], + ['epmu', + '[B+ -> (eta -> (J/psi(1S) -> ^e+ e-) gamma) mu+]CC', + '[B+ -> (eta -> (J/psi(1S) -> e+ e-) gamma) ^mu+]CC'], + ['emmu', + '[B+ -> (eta -> (J/psi(1S) -> e+ ^e-) gamma) mu+]CC', + '[B+ -> (eta -> (J/psi(1S) -> e+ e-) gamma) ^mu+]CC']) + else: + raise ValueError('Decay decsriptor is not valid to call TupleToolDOCA') + + DOCA = dtt.B.addTupleTool("TupleToolDOCA") + DOCA.Name = list(doca_name) + DOCA.Location1 = list(location1) + DOCA.Location2 = list(location2) + +def JpsiConstraints(dtt): + ''' + Add variables with constraints on the di-electron mass + The variables listed allow to calculate a fully constrained m2miss offline + ''' + + if dtt.Decay not in [ + '[B+ -> ^(J/psi(1S) -> ^e+ ^e-) ^mu+]CC', + '[B+ -> ^(J/psi(1S) -> ^e+ ^e+) ^[mu+]cc]CC']: + raise ValueError( + 'Decay decsriptor is not valid to call JpsiConstraints') + + DTF_vars = { + 'M': 'M', + 'PX': 'PX', + 'PY': 'PY', + 'PZ': 'PZ', + 'PT': 'PT', + + 'Jpsi_M': 'CHILD(1, M)', + 'Jpsi_PX': 'CHILD(1, PX)', + 'Jpsi_PY': 'CHILD(1, PY)', + 'Jpsi_PZ': 'CHILD(1, PZ)', + + 'X_travelled': 'VFASPF(VX)-BPV(VX)', + 'Y_travelled': 'VFASPF(VY)-BPV(VY)', + 'Z_travelled': 'VFASPF(VZ)-BPV(VZ)', + + 'PVCORRM': 'BPVCORRM', + 'PVCORRMERR': 'BPVCORRMERROR', + } + + JpsiTuple = dtt.B.addTupleTool(LoKi__Hybrid__Dict2Tuple, 'JpsiTuple') + + JpsiTuple.addTool(LoKi__Hybrid__DTFDict, 'JpsiDTF') + JpsiTuple.Source = 'LoKi::Hybrid::DTFDict/JpsiDTF' + JpsiTuple.NumVar = 15 + JpsiTuple.JpsiDTF.constrainToOriginVertex = False + JpsiTuple.JpsiDTF.daughtersToConstrain = ['J/psi(1S)'] + + JpsiTuple.JpsiDTF.addTool(LoKi__Hybrid__DictOfFunctors, 'dict_Jpsi') + JpsiTuple.JpsiDTF.Source = 'LoKi::Hybrid::DictOfFunctors/dict_Jpsi' + JpsiTuple.JpsiDTF.dict_Jpsi.Variables = DTF_vars + + +def TupleToolConeIsolation(dtt): + '''Add cone isolation variables''' + + dtt.Jpsi.addTupleTool('TupleToolConeIsolation') + + dtt.Jpsi.TupleToolConeIsolation.ExtraParticlesLocation = 'Phys/StdAllNoPIDsPions/Particles' + dtt.Jpsi.TupleToolConeIsolation.MaxPtParticlesLocation = 'Phys/StdAllLoosePions/Particles' + dtt.Jpsi.TupleToolConeIsolation.ExtraPhotonsLocation = 'Phys/StdLooseAllPhotons/Particles' + + dtt.Jpsi.TupleToolConeIsolation.MinConeSize = 0.4 + dtt.Jpsi.TupleToolConeIsolation.MaxConeSize = 0.6 + dtt.Jpsi.TupleToolConeIsolation.SizeStep = 0.2 + + dtt.Jpsi.TupleToolConeIsolation.FillCharged = True + dtt.Jpsi.TupleToolConeIsolation.FillNeutral = True + + dtt.Jpsi.TupleToolConeIsolation.FillAsymmetry = True + dtt.Jpsi.TupleToolConeIsolation.FillDeltas = True + dtt.Jpsi.TupleToolConeIsolation.FillIsolation = True + + dtt.Jpsi.TupleToolConeIsolation.FillMaxPt = True + dtt.Jpsi.TupleToolConeIsolation.FillComponents = True + dtt.Jpsi.TupleToolConeIsolation.FillPi0Info = False + dtt.Jpsi.TupleToolConeIsolation.FillMergedPi0Info = False + + +def TupleToolSubstitution(dtt): + '''Add variables for lepton -> hadron (double-)substitutions''' + + substitutions = [ + 'mu+ => K+', + 'mu+ => pi+', + 'mu+ => p+' + ] + SubstTool = dtt.B.addTupleTool('TupleToolSubMass/B_SubMass') + SubstTool.Substitution += substitutions +