From dbf25e3b7d6308c07257d8936fcde83c0d8ca2d2 Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Tue, 18 Jul 2023 10:43:13 +0200
Subject: [PATCH 1/7] add selection of Bu2Dst0MuNu

---
 B2munuee/Bu2Dst0MuNu_options.py | 127 ++++++++++++++++++++
 B2munuee/__init__.py            |   0
 B2munuee/info.yaml              |  25 ++++
 B2munuee/my_utils.py            | 206 ++++++++++++++++++++++++++++++++
 4 files changed, 358 insertions(+)
 create mode 100644 B2munuee/Bu2Dst0MuNu_options.py
 create mode 100644 B2munuee/__init__.py
 create mode 100644 B2munuee/info.yaml
 create mode 100644 B2munuee/my_utils.py

diff --git a/B2munuee/Bu2Dst0MuNu_options.py b/B2munuee/Bu2Dst0MuNu_options.py
new file mode 100644
index 0000000000..18201cd837
--- /dev/null
+++ b/B2munuee/Bu2Dst0MuNu_options.py
@@ -0,0 +1,127 @@
+
+from Configurables import DaVinci
+from Configurables import DecayTreeTuple
+from Configurables import GaudiSequencer
+from Configurables import TupleToolTISTOS, BackgroundCategory
+from Configurables import LoKi__Hybrid__TupleTool
+
+from PhysSelPython.Wrappers import SelectionSequence
+
+from PhysSelPython.Wrappers import Selection, DataOnDemand, AutomaticData
+from Configurables import FilterInTrees, FilterDesktop, CombineParticles
+from Configurables import DiElectronMaker
+
+from CommonParticles.Utils import updateDoD
+from GaudiKernel.SystemOfUnits import MeV
+
+from DecayTreeTuple.Configuration import addTupleTool, setDescriptorTemplate
+
+from B2munuee import my_utils as utils
+
+"""
+Additional cuts wrt. B2Dst0MuNuDst0D0GammaLine to align with B23MuNu_MueeLine:
+
+leptons: TRCHI2DOF < 3 & TRGHP < 0.3 & (PID{lepton} - PIDK) > 0.0
+B candidate: BPVVDCHI2 > 30 & PT > 2000 & BPVCORRM > 2500 & BPVCORRM < 10000
+"""
+
+def GetMuon(stream, loc):
+    muon_cut = ("('mu+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDmu - PIDK) > 0.0)")
+
+    muon_filter = FilterInTrees('MuonFilter', Code=muon_cut)
+    muon = Selection("MuonsForB2DstMuNu",
+                     Algorithm=muon_filter,
+                     RequiredSelections=[DataOnDemand(Location=loc)])
+    return muon
+
+def GetDiLepton(stream, loc):
+    electron_cut = ("('e+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDe - PIDK) > 0.0)")
+
+    electrons_filter = FilterInTrees('ElectronsForB2Dst0MuNu',
+                                    Inputs=[loc],
+                                    Code=electron_cut)
+    updateDoD(electrons_filter)
+
+    #DiElectron
+    dieLL = DiElectronMaker('DiElectron')
+    dieLL.Particle = "J/psi(1S)"
+
+    dieLL.ElectronInputs = [f'Phys/ElectronsForB2Dst0MuNu/Particles']
+
+    dieLL.DiElectronMassMax = 300.0*MeV
+    dieLL.DiElectronMassMin = 0.0*MeV
+    dieLL.DiElectronPtMin = 0.0*MeV
+    dieLL.ElectronPtMin = 0.0*MeV
+
+    DiElectron = Selection('SelectDiElectron', Algorithm=dieLL)
+
+    Filter_DiElectron = FilterDesktop('DiElectronFilter', 
+                                    Code = "(M < 250) & (VFASPF(VCHI2/VDOF) < 5)")
+    Sel_DiElectron = Selection('Sel_DiElectron', 
+                                Algorithm = Filter_DiElectron, 
+                                RequiredSelections=[DiElectron])
+
+    return Sel_DiElectron
+
+def GetD0(stream, loc):
+    d0_filter = FilterInTrees('D0Filter', Code="('D0' == ABSID)")
+    D0 = Selection("D0ForB2DstMuNu",
+                   Algorithm=d0_filter,
+                   RequiredSelections=[DataOnDemand(Location=loc)])
+
+    return D0
+
+def CombineAll(D0, DiLepton, muon):
+    #D*0
+    Dst_combine = CombineParticles('CombineDst', 
+                                    DecayDescriptor="[D*(2007)0 -> D0 J/psi(1S)]cc", 
+                                    CombinationCut="(AM - ACHILD(1, M) < 350*MeV)",
+                                    MotherCut="(VFASPF(VCHI2/VDOF) < 6)")
+    Dst = Selection('SelectDst', Algorithm=Dst_combine, RequiredSelections=[D0, DiLepton])
+
+    #B 
+    B_combine = CombineParticles('CombineB',
+                                DecayDescriptor = "[B- -> D*(2007)0 mu-]cc",
+                                CombinationCut = "(AM > 2200*MeV) & (AM < 6000*MeV)",
+                                MotherCut = "(VFASPF(VCHI2/VDOF) < 6) & (BPVVDCHI2 > 30.) & (BPVDIRA > 0.99) & (PT > 2000) & " \
+                                    "(BPVCORRM > 2500) & (BPVCORRM < 10000)")
+    
+    B = Selection('SelectB', Algorithm=B_combine, RequiredSelections=[Dst, muon])
+    return B
+
+
+
+stream = 'Semileptonic'
+loc = f'/Event/{stream}/Phys/B2Dst0MuNuDst0D0GammaLine/Particles'
+
+# select muons from stripping
+muon = GetMuon(stream, loc)
+
+# select electrons from stripping and run DiElectronMaker
+DiLepton = GetDiLepton(stream, loc)
+
+# select D0 from K and pi from std particle containers
+D0 = GetD0(stream, loc)
+
+# Combine particles
+B = CombineAll(D0, DiLepton, muon)
+
+# make nTuple
+# for B2munuee the convention is 'ep' and 'mu' are of same sign
+dtt = DecayTreeTuple('B2MuNuEE')
+dtt.setDescriptorTemplate(
+            '${B}[B- -> ${Dst}(D*(2007)0 -> ${D0}(D0 -> ${K}K- ${pi}pi+) ${Jpsi}(J/psi(1S) -> ${em}e+ ${ep}e-)) ${mu}mu-]CC')
+Seq = SelectionSequence('Sequence', TopSelection=B)
+dtt.Inputs = Seq.outputLocations()
+
+_seq = GaudiSequencer('MySequencer')
+_seq.Members += [Seq.sequence(), dtt]
+DaVinci().appendToMainSequence([_seq])
+
+utils.DefaultToolList(dtt)
+utils.TriggerToolList(dtt)
+utils.DefaultLoKi(dtt)
+utils.AddDOCA(dtt)
+utils.TupleToolConeIsolation(dtt)
+utils.TupleToolSubstitution(dtt)
+utils.TupleToolVeto(dtt)
diff --git a/B2munuee/__init__.py b/B2munuee/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/B2munuee/info.yaml b/B2munuee/info.yaml
new file mode 100644
index 0000000000..7f243fb4c8
--- /dev/null
+++ b/B2munuee/info.yaml
@@ -0,0 +1,25 @@
+defaults:
+    application: DaVinci/v46r4
+    wg: SL
+    automatically_configure: yes
+    turbo: no
+    inform:
+        - fabian.christoph.glaser@cern.ch
+
+{%- set real_datasets = [
+    ('16', '28r2'),
+    ('17', '29r2p1'),
+    ('18', '34r0p1'),
+]%}
+
+{%- for year, stripping in real_datasets %}
+  {%- for polarity in ['MagDown','MagUp'] %}
+
+20{{year}}_{{polarity}}_Bu2Dst0MuNu:
+    input:
+        bk_query: /LHCb/Collision{{year}}/Beam6500GeV-VeloClosed-{{polarity}}/Real Data/Reco{{year}}/Stripping{{stripping}}/90000000/SEMILEPTONIC.DST
+    output: Bu2Dst0MuNu.ROOT
+    options: Bu2Dst0MuNu_options.py
+
+  {%- endfor %}
+{%- endfor %}
\ No newline at end of file
diff --git a/B2munuee/my_utils.py b/B2munuee/my_utils.py
new file mode 100644
index 0000000000..c192bf5748
--- /dev/null
+++ b/B2munuee/my_utils.py
@@ -0,0 +1,206 @@
+from Configurables import TupleToolTISTOS
+from Configurables import LoKi__Hybrid__TupleTool
+
+
+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
+
+    # add info on missing momentum
+    NuReco = dtt.B.addTupleTool("TupleToolNeutrinoReco")
+    NuReco.Verbose = False
+
+    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 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 AddDOCA(dtt):
+    '''
+    Add distance of closest approach DOCA for the three lepton tracks
+    '''
+    
+    doca_name, location1, location2 = zip(
+            ['epem', 
+             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> ^e+ e-)) mu-]CC',
+             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ ^e-)) mu-]CC'],
+            ['epmu', 
+             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ ^e-)) mu-]CC',
+             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ e-)) ^mu-]CC'],
+            ['emmu', 
+             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> ^e+ e-)) mu-]CC',
+             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ e-)) ^mu-]CC'])
+
+    DOCA = dtt.B.addTupleTool("TupleToolDOCA")
+    DOCA.Name = list(doca_name)
+    DOCA.Location1 = list(location1)
+    DOCA.Location2 = list(location2)
+
+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
+
+def TupleToolVeto(dtt):
+    ''' Add tool for pi0 veto for single photons'''
+
+    vetoContainersPi0 = ['Phys/StdLoosePi02gg/Particles',
+                         'Phys/StdLooseResolvedPi0/Particles',
+                         'Phys/StdLooseMergedPi0/Particles']
+
+    vetoTool = dtt.Jpsi.addTupleTool("TupleToolVeto")
+    vetoTool.Particle = 'J/psi(1S)'
+    vetoTool.Veto['_Pi0'] = vetoContainersPi0
-- 
GitLab


From a61104c60ea427128b24438ce9f8b7c1bb360dbb Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Tue, 18 Jul 2023 16:22:43 +0200
Subject: [PATCH 2/7] few style changes

---
 B2munuee/Bu2Dst0MuNu_options.py | 123 +++++++++++++++++---------------
 1 file changed, 65 insertions(+), 58 deletions(-)

diff --git a/B2munuee/Bu2Dst0MuNu_options.py b/B2munuee/Bu2Dst0MuNu_options.py
index 18201cd837..e4bb4ac8f1 100644
--- a/B2munuee/Bu2Dst0MuNu_options.py
+++ b/B2munuee/Bu2Dst0MuNu_options.py
@@ -1,3 +1,12 @@
+"""
+Selection of B- -> D*0 (-> D0 gamma) mu nu as reference for B- -> mu nu gamma
+Use B2Dst0MuNuDst0D0GammaLine to select the reference channel
+
+Additional cuts wrt. B2Dst0MuNuDst0D0GammaLine to align with B23MuNu_MueeLine:
+
+leptons: TRCHI2DOF < 3 & TRGHP < 0.3 & (PID{lepton} - PIDK) > 0.0
+B candidate: BPVVDCHI2 > 30 & PT > 2000 & BPVCORRM > 2500 & BPVCORRM < 10000
+"""
 
 from Configurables import DaVinci
 from Configurables import DecayTreeTuple
@@ -18,99 +27,97 @@ from DecayTreeTuple.Configuration import addTupleTool, setDescriptorTemplate
 
 from B2munuee import my_utils as utils
 
-"""
-Additional cuts wrt. B2Dst0MuNuDst0D0GammaLine to align with B23MuNu_MueeLine:
-
-leptons: TRCHI2DOF < 3 & TRGHP < 0.3 & (PID{lepton} - PIDK) > 0.0
-B candidate: BPVVDCHI2 > 30 & PT > 2000 & BPVCORRM > 2500 & BPVCORRM < 10000
-"""
 
-def GetMuon(stream, loc):
-    muon_cut = ("('mu+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDmu - PIDK) > 0.0)")
+def GetMuon(loc):
+    muon_cut = (
+        "('mu+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDmu - PIDK) > 0.0)")
 
     muon_filter = FilterInTrees('MuonFilter', Code=muon_cut)
-    muon = Selection("MuonsForB2DstMuNu",
-                     Algorithm=muon_filter,
+    return Selection("MuonsForB2DstMuNu", Algorithm=muon_filter,
                      RequiredSelections=[DataOnDemand(Location=loc)])
-    return muon
 
-def GetDiLepton(stream, loc):
-    electron_cut = ("('e+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDe - PIDK) > 0.0)")
 
-    electrons_filter = FilterInTrees('ElectronsForB2Dst0MuNu',
-                                    Inputs=[loc],
-                                    Code=electron_cut)
-    updateDoD(electrons_filter)
-
-    #DiElectron
+def GetDiLepton(loc, tes):
     dieLL = DiElectronMaker('DiElectron')
     dieLL.Particle = "J/psi(1S)"
 
-    dieLL.ElectronInputs = [f'Phys/ElectronsForB2Dst0MuNu/Particles']
+    dieLL.ElectronInputs = [f'Phys/{tes}/Particles']
 
-    dieLL.DiElectronMassMax = 300.0*MeV
-    dieLL.DiElectronMassMin = 0.0*MeV
-    dieLL.DiElectronPtMin = 0.0*MeV
-    dieLL.ElectronPtMin = 0.0*MeV
+    dieLL.DiElectronMassMax = 300.0 * MeV
+    dieLL.DiElectronMassMin = 0.0 * MeV
+    dieLL.DiElectronPtMin = 0.0 * MeV
+    dieLL.ElectronPtMin = 0.0 * MeV
 
-    DiElectron = Selection('SelectDiElectron', Algorithm=dieLL)
+    dielectron = Selection('SelectDiElectron', Algorithm=dieLL)
 
-    Filter_DiElectron = FilterDesktop('DiElectronFilter', 
-                                    Code = "(M < 250) & (VFASPF(VCHI2/VDOF) < 5)")
-    Sel_DiElectron = Selection('Sel_DiElectron', 
-                                Algorithm = Filter_DiElectron, 
-                                RequiredSelections=[DiElectron])
+    dielectron_filter = FilterDesktop('DiElectronFilter',
+                                      Code="(M < 250) & (VFASPF(VCHI2/VDOF) < 5)")
 
-    return Sel_DiElectron
+    return Selection('Sel_DiElectron', Algorithm=dielectron_filter,
+                     RequiredSelections=[dielectron])
 
-def GetD0(stream, loc):
-    d0_filter = FilterInTrees('D0Filter', Code="('D0' == ABSID)")
-    D0 = Selection("D0ForB2DstMuNu",
-                   Algorithm=d0_filter,
-                   RequiredSelections=[DataOnDemand(Location=loc)])
 
-    return D0
+def GetD0(loc):
+    d0_filter = FilterInTrees('D0Filter', Code="('D0' == ABSID)")
 
-def CombineAll(D0, DiLepton, muon):
-    #D*0
-    Dst_combine = CombineParticles('CombineDst', 
-                                    DecayDescriptor="[D*(2007)0 -> D0 J/psi(1S)]cc", 
-                                    CombinationCut="(AM - ACHILD(1, M) < 350*MeV)",
-                                    MotherCut="(VFASPF(VCHI2/VDOF) < 6)")
-    Dst = Selection('SelectDst', Algorithm=Dst_combine, RequiredSelections=[D0, DiLepton])
+    return Selection("D0ForB2DstMuNu", Algorithm=d0_filter,
+                     RequiredSelections=[DataOnDemand(Location=loc)])
 
-    #B 
-    B_combine = CombineParticles('CombineB',
-                                DecayDescriptor = "[B- -> D*(2007)0 mu-]cc",
-                                CombinationCut = "(AM > 2200*MeV) & (AM < 6000*MeV)",
-                                MotherCut = "(VFASPF(VCHI2/VDOF) < 6) & (BPVVDCHI2 > 30.) & (BPVDIRA > 0.99) & (PT > 2000) & " \
-                                    "(BPVCORRM > 2500) & (BPVCORRM < 10000)")
-    
-    B = Selection('SelectB', Algorithm=B_combine, RequiredSelections=[Dst, muon])
-    return B
 
+def CombineAll(D0, DiLepton, muon):
+    # Make and select D*0 -> D0 gamma
+    dst_combine = CombineParticles('CombineDst',
+                                   DecayDescriptor="[D*(2007)0 -> D0 J/psi(1S)]cc",
+                                   CombinationCut="(AM - ACHILD(1, M) < 350*MeV)",
+                                   MotherCut="(VFASPF(VCHI2/VDOF) < 6)")
+    dst_selection = Selection(
+        'SelectDst',
+        Algorithm=dst_combine,
+        RequiredSelections=[
+            D0,
+            DiLepton])
+
+    # Make and select B- -> D*0 mu-
+    b_mother_cut = "(VFASPF(VCHI2/VDOF) < 6) & (BPVVDCHI2 > 30.) & (BPVDIRA > 0.99) & " \
+        "(PT > 2000) & (BPVCORRM > 2500) & (BPVCORRM < 10000)"
+
+    b_combine = CombineParticles('CombineB',
+                                 DecayDescriptor="[B- -> D*(2007)0 mu-]cc",
+                                 CombinationCut="(AM > 2200*MeV) & (AM < 6000*MeV)",
+                                 MotherCut=b_mother_cut)
+
+    return Selection('SelectB', Algorithm=b_combine,
+                     RequiredSelections=[dst_selection, muon])
 
 
 stream = 'Semileptonic'
 loc = f'/Event/{stream}/Phys/B2Dst0MuNuDst0D0GammaLine/Particles'
 
 # select muons from stripping
-muon = GetMuon(stream, loc)
+muonFromB2Dst0MuNu = GetMuon(loc)
+
+# filter electrons from stripping selection to be passed to DiElectronMaker
+electron_cut = "('e+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDe - PIDK) > 0.0)"
+electrons_tes = 'ElectronsFromB2Dst0MuNu'
+electrons_filter = FilterInTrees(electrons_tes,
+                                 Inputs=[loc],
+                                 Code=electron_cut)
+updateDoD(electrons_filter)
 
 # select electrons from stripping and run DiElectronMaker
-DiLepton = GetDiLepton(stream, loc)
+DiLeptonFromB2DstMuNu = GetDiLepton(loc, electrons_tes)
 
 # select D0 from K and pi from std particle containers
-D0 = GetD0(stream, loc)
+D0FromB2DstMuNu = GetD0(loc)
 
 # Combine particles
-B = CombineAll(D0, DiLepton, muon)
+B = CombineAll(D0FromB2DstMuNu, DiLeptonFromB2DstMuNu, muonFromB2Dst0MuNu)
 
 # make nTuple
 # for B2munuee the convention is 'ep' and 'mu' are of same sign
 dtt = DecayTreeTuple('B2MuNuEE')
 dtt.setDescriptorTemplate(
-            '${B}[B- -> ${Dst}(D*(2007)0 -> ${D0}(D0 -> ${K}K- ${pi}pi+) ${Jpsi}(J/psi(1S) -> ${em}e+ ${ep}e-)) ${mu}mu-]CC')
+    '${B}[B- -> ${Dst}(D*(2007)0 -> ${D0}(D0 -> ${K}K- ${pi}pi+) ${Jpsi}(J/psi(1S) -> ${em}e+ ${ep}e-)) ${mu}mu-]CC')
 Seq = SelectionSequence('Sequence', TopSelection=B)
 dtt.Inputs = Seq.outputLocations()
 
-- 
GitLab


From e53694540b8f903f082ef97319ec8d04974b1658 Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Mon, 18 Sep 2023 13:50:37 +0200
Subject: [PATCH 3/7] new production for latest MC samples

---
 B2munuee/B2Vub_MCoptions.py     |  11 ++
 B2munuee/Bc2munucc_MCoptions.py |  10 ++
 B2munuee/Bu2Dst0MuNu_options.py | 134 ----------------
 B2munuee/Bu2munuee_MCoptions.py |   8 +
 B2munuee/info.yaml              |  84 ++++++++--
 B2munuee/my_selections.py       | 264 ++++++++++++++++++++++++++++++++
 B2munuee/my_utils.py            | 137 +++++++++++++++--
 7 files changed, 488 insertions(+), 160 deletions(-)
 create mode 100644 B2munuee/B2Vub_MCoptions.py
 create mode 100644 B2munuee/Bc2munucc_MCoptions.py
 delete mode 100644 B2munuee/Bu2Dst0MuNu_options.py
 create mode 100644 B2munuee/Bu2munuee_MCoptions.py
 create mode 100644 B2munuee/my_selections.py

diff --git a/B2munuee/B2Vub_MCoptions.py b/B2munuee/B2Vub_MCoptions.py
new file mode 100644
index 0000000000..e0288987eb
--- /dev/null
+++ b/B2munuee/B2Vub_MCoptions.py
@@ -0,0 +1,11 @@
+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
+selections.MySelection(trees, isMC, addJpsiConstraints)
\ No newline at end of file
diff --git a/B2munuee/Bc2munucc_MCoptions.py b/B2munuee/Bc2munucc_MCoptions.py
new file mode 100644
index 0000000000..f99d80384a
--- /dev/null
+++ b/B2munuee/Bc2munucc_MCoptions.py
@@ -0,0 +1,10 @@
+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
+selections.MySelection(trees, isMC, addJpsiConstraints)
\ No newline at end of file
diff --git a/B2munuee/Bu2Dst0MuNu_options.py b/B2munuee/Bu2Dst0MuNu_options.py
deleted file mode 100644
index e4bb4ac8f1..0000000000
--- a/B2munuee/Bu2Dst0MuNu_options.py
+++ /dev/null
@@ -1,134 +0,0 @@
-"""
-Selection of B- -> D*0 (-> D0 gamma) mu nu as reference for B- -> mu nu gamma
-Use B2Dst0MuNuDst0D0GammaLine to select the reference channel
-
-Additional cuts wrt. B2Dst0MuNuDst0D0GammaLine to align with B23MuNu_MueeLine:
-
-leptons: TRCHI2DOF < 3 & TRGHP < 0.3 & (PID{lepton} - PIDK) > 0.0
-B candidate: BPVVDCHI2 > 30 & PT > 2000 & BPVCORRM > 2500 & BPVCORRM < 10000
-"""
-
-from Configurables import DaVinci
-from Configurables import DecayTreeTuple
-from Configurables import GaudiSequencer
-from Configurables import TupleToolTISTOS, BackgroundCategory
-from Configurables import LoKi__Hybrid__TupleTool
-
-from PhysSelPython.Wrappers import SelectionSequence
-
-from PhysSelPython.Wrappers import Selection, DataOnDemand, AutomaticData
-from Configurables import FilterInTrees, FilterDesktop, CombineParticles
-from Configurables import DiElectronMaker
-
-from CommonParticles.Utils import updateDoD
-from GaudiKernel.SystemOfUnits import MeV
-
-from DecayTreeTuple.Configuration import addTupleTool, setDescriptorTemplate
-
-from B2munuee import my_utils as utils
-
-
-def GetMuon(loc):
-    muon_cut = (
-        "('mu+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDmu - PIDK) > 0.0)")
-
-    muon_filter = FilterInTrees('MuonFilter', Code=muon_cut)
-    return Selection("MuonsForB2DstMuNu", Algorithm=muon_filter,
-                     RequiredSelections=[DataOnDemand(Location=loc)])
-
-
-def GetDiLepton(loc, tes):
-    dieLL = DiElectronMaker('DiElectron')
-    dieLL.Particle = "J/psi(1S)"
-
-    dieLL.ElectronInputs = [f'Phys/{tes}/Particles']
-
-    dieLL.DiElectronMassMax = 300.0 * MeV
-    dieLL.DiElectronMassMin = 0.0 * MeV
-    dieLL.DiElectronPtMin = 0.0 * MeV
-    dieLL.ElectronPtMin = 0.0 * MeV
-
-    dielectron = Selection('SelectDiElectron', Algorithm=dieLL)
-
-    dielectron_filter = FilterDesktop('DiElectronFilter',
-                                      Code="(M < 250) & (VFASPF(VCHI2/VDOF) < 5)")
-
-    return Selection('Sel_DiElectron', Algorithm=dielectron_filter,
-                     RequiredSelections=[dielectron])
-
-
-def GetD0(loc):
-    d0_filter = FilterInTrees('D0Filter', Code="('D0' == ABSID)")
-
-    return Selection("D0ForB2DstMuNu", Algorithm=d0_filter,
-                     RequiredSelections=[DataOnDemand(Location=loc)])
-
-
-def CombineAll(D0, DiLepton, muon):
-    # Make and select D*0 -> D0 gamma
-    dst_combine = CombineParticles('CombineDst',
-                                   DecayDescriptor="[D*(2007)0 -> D0 J/psi(1S)]cc",
-                                   CombinationCut="(AM - ACHILD(1, M) < 350*MeV)",
-                                   MotherCut="(VFASPF(VCHI2/VDOF) < 6)")
-    dst_selection = Selection(
-        'SelectDst',
-        Algorithm=dst_combine,
-        RequiredSelections=[
-            D0,
-            DiLepton])
-
-    # Make and select B- -> D*0 mu-
-    b_mother_cut = "(VFASPF(VCHI2/VDOF) < 6) & (BPVVDCHI2 > 30.) & (BPVDIRA > 0.99) & " \
-        "(PT > 2000) & (BPVCORRM > 2500) & (BPVCORRM < 10000)"
-
-    b_combine = CombineParticles('CombineB',
-                                 DecayDescriptor="[B- -> D*(2007)0 mu-]cc",
-                                 CombinationCut="(AM > 2200*MeV) & (AM < 6000*MeV)",
-                                 MotherCut=b_mother_cut)
-
-    return Selection('SelectB', Algorithm=b_combine,
-                     RequiredSelections=[dst_selection, muon])
-
-
-stream = 'Semileptonic'
-loc = f'/Event/{stream}/Phys/B2Dst0MuNuDst0D0GammaLine/Particles'
-
-# select muons from stripping
-muonFromB2Dst0MuNu = GetMuon(loc)
-
-# filter electrons from stripping selection to be passed to DiElectronMaker
-electron_cut = "('e+' == ABSID) & (TRCHI2DOF < 3) & (TRGHP < 0.3) & ((PIDe - PIDK) > 0.0)"
-electrons_tes = 'ElectronsFromB2Dst0MuNu'
-electrons_filter = FilterInTrees(electrons_tes,
-                                 Inputs=[loc],
-                                 Code=electron_cut)
-updateDoD(electrons_filter)
-
-# select electrons from stripping and run DiElectronMaker
-DiLeptonFromB2DstMuNu = GetDiLepton(loc, electrons_tes)
-
-# select D0 from K and pi from std particle containers
-D0FromB2DstMuNu = GetD0(loc)
-
-# Combine particles
-B = CombineAll(D0FromB2DstMuNu, DiLeptonFromB2DstMuNu, muonFromB2Dst0MuNu)
-
-# make nTuple
-# for B2munuee the convention is 'ep' and 'mu' are of same sign
-dtt = DecayTreeTuple('B2MuNuEE')
-dtt.setDescriptorTemplate(
-    '${B}[B- -> ${Dst}(D*(2007)0 -> ${D0}(D0 -> ${K}K- ${pi}pi+) ${Jpsi}(J/psi(1S) -> ${em}e+ ${ep}e-)) ${mu}mu-]CC')
-Seq = SelectionSequence('Sequence', TopSelection=B)
-dtt.Inputs = Seq.outputLocations()
-
-_seq = GaudiSequencer('MySequencer')
-_seq.Members += [Seq.sequence(), dtt]
-DaVinci().appendToMainSequence([_seq])
-
-utils.DefaultToolList(dtt)
-utils.TriggerToolList(dtt)
-utils.DefaultLoKi(dtt)
-utils.AddDOCA(dtt)
-utils.TupleToolConeIsolation(dtt)
-utils.TupleToolSubstitution(dtt)
-utils.TupleToolVeto(dtt)
diff --git a/B2munuee/Bu2munuee_MCoptions.py b/B2munuee/Bu2munuee_MCoptions.py
new file mode 100644
index 0000000000..042f16c834
--- /dev/null
+++ b/B2munuee/Bu2munuee_MCoptions.py
@@ -0,0 +1,8 @@
+from B2munuee import my_selections as selections
+
+# B+ -> mu nu eta' (-> e e gamma)
+
+trees = ['default', 'SameSign']
+isMC = True
+addJpsiConstraints = False
+selections.MySelection(trees, isMC, addJpsiConstraints)
\ No newline at end of file
diff --git a/B2munuee/info.yaml b/B2munuee/info.yaml
index 7f243fb4c8..4a349339ed 100644
--- a/B2munuee/info.yaml
+++ b/B2munuee/info.yaml
@@ -6,20 +6,86 @@ defaults:
     inform:
         - fabian.christoph.glaser@cern.ch
 
-{%- set real_datasets = [
-    ('16', '28r2'),
-    ('17', '29r2p1'),
-    ('18', '34r0p1'),
+{%- 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, stripping in real_datasets %}
+{%- for year, sim, trig, reco, turbo, strip in mc_datasets %}
   {%- for polarity in ['MagDown','MagUp'] %}
 
-20{{year}}_{{polarity}}_Bu2Dst0MuNu:
+    
+{{year}}_{{polarity}}_MC_Bu2munuetap2eeg:
+    #B+ -> mu nu eta' (-> e+ e- gamma)
     input:
-        bk_query: /LHCb/Collision{{year}}/Beam6500GeV-VeloClosed-{{polarity}}/Real Data/Reco{{year}}/Stripping{{stripping}}/90000000/SEMILEPTONIC.DST
-    output: Bu2Dst0MuNu.ROOT
-    options: Bu2Dst0MuNu_options.py
+        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 %}
\ No newline at end of file
diff --git a/B2munuee/my_selections.py b/B2munuee/my_selections.py
new file mode 100644
index 0000000000..1369a39db5
--- /dev/null
+++ b/B2munuee/my_selections.py
@@ -0,0 +1,264 @@
+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, CombineSelection
+
+from CommonParticles.Utils import updateDoD
+
+from GaudiKernel.SystemOfUnits import MeV
+from B2munuee import my_utils as utils
+
+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 RunDiElectronMaker(tree):
+    '''
+    Run DiElectronMaker on the electrons that passed the stripping selection
+    This instance does not use the proto-particles
+    '''
+    dieLL = DiElectronMaker('DiElectron-' + tree)
+    dieLL.Particle = "J/psi(1S)"
+
+    if tree in ['default', 'SameSign', 'pions', 'eta']:
+        dieLL.ElectronInputs = [f'Phys/Electrons_{tree}/Particles']
+    else:
+        raise ValueError(
+            f'{tree} is not a valid option ro run DiElectronMaker')
+
+    # 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
+
+    if tree in ['SameSign']:
+        dieLL.OppositeSign = False
+    else:
+        dieLL.OppositeSign = True
+
+    DiElectron = Selection('DiElectron_' + tree, Algorithm=dieLL)
+
+    return DiElectron
+
+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, Jpsi):
+    '''
+    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)
+
+    muons = AutomaticData(f'Phys/Muons_{tree}/Particles')
+
+    if tree in ['pions']:
+        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']:
+        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']:
+        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']:
+        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(
+            f'{tree} is no valid tree for MakeMother')
+
+    return B_sel
+
+
+def MakeNTuples(B, name):
+    '''
+    Make nTuples with di-lepton object
+    '''
+    dtt = DecayTreeTuple('B2MuNuEE_' + name)
+    if name in ['default']:
+        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(
+            f'{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, addJpsiConstraints):
+    if isMC:
+        stream = 'AllStreams'
+    else:
+        stream = 'Semileptonic'
+
+    for tree in trees:
+        if tree not in ['default', 'SameSign', 'pions', 'eta']:
+            raise ValueError(
+                f'Selection for specified tree {tree} is not supported')
+
+        # select muons from stripping or std container
+        muon_filter = FilterInTrees('Muons_'+tree,
+                            Inputs=[f'/Event/{stream}/Phys/B23MuNu_MueeLine/Particles'],
+                            Code="('mu+' == ABSID)")
+        updateDoD(muon_filter)
+
+        # select electrons from stripping or std container
+        electrons_filter = FilterInTrees('Electrons_'+tree,
+                            Inputs=[f'/Event/{stream}/Phys/B23MuNu_MueeLine/Particles'],
+                            Code="('e+' == ABSID)")
+        updateDoD(electrons_filter)
+
+        # run dieelctron maker
+        DiLepton = RunDiElectronMaker(tree)
+
+        # make Mother particle
+        mother = MakeMother(tree, DiLepton)
+
+        # make nTuple
+        dtt = MakeNTuples(mother, tree)
+        
+        # now fill the tuples
+        utils.DefaultToolList(dtt)
+        if isMC:
+            utils.MCToolList(dtt)
+        utils.TriggerToolList(dtt)
+        utils.DefaultLoKi(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
index c192bf5748..fe69168d0b 100644
--- a/B2munuee/my_utils.py
+++ b/B2munuee/my_utils.py
@@ -1,5 +1,5 @@
-from Configurables import TupleToolTISTOS
-from Configurables import LoKi__Hybrid__TupleTool
+from Configurables import TupleToolTISTOS, BackgroundCategory
+from Configurables import LoKi__Hybrid__TupleTool, LoKi__Hybrid__Dict2Tuple, LoKi__Hybrid__DTFDict, LoKi__Hybrid__DictOfFunctors
 
 
 def DefaultToolList(dtt):
@@ -34,15 +34,17 @@ def DefaultToolList(dtt):
     IsoGeneric = dtt.addTupleTool("TupleToolIsoGeneric")
     IsoGeneric.Verbose = True
 
-    # add info on missing momentum
-    NuReco = dtt.B.addTupleTool("TupleToolNeutrinoReco")
-    NuReco.Verbose = False
-
     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'''
@@ -136,27 +138,129 @@ def DefaultLoKi(dtt):
         '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
     '''
-    
-    doca_name, location1, location2 = zip(
+    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- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> ^e+ e-)) mu-]CC',
-             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ ^e-)) mu-]CC'],
+             '[B+ -> (J/psi(1S) -> ^e+ e+) [mu+]cc]CC',
+             '[B+ -> (J/psi(1S) -> e+ ^e+) [mu+]cc]CC'],
             ['epmu', 
-             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ ^e-)) mu-]CC',
-             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ e-)) ^mu-]CC'],
+             '[B+ -> (J/psi(1S) -> ^e+ e+) [mu+]cc]CC',
+             '[B+ -> (J/psi(1S) -> e+ e+) ^[mu+]cc]CC'],
             ['emmu', 
-             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> ^e+ e-)) mu-]CC',
-             '[B- -> (D*(2007)0 -> (D0 -> K- pi+) (J/psi(1S) -> e+ e-)) ^mu-]CC'])
+             '[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'''
 
@@ -197,9 +301,8 @@ def TupleToolSubstitution(dtt):
 def TupleToolVeto(dtt):
     ''' Add tool for pi0 veto for single photons'''
 
-    vetoContainersPi0 = ['Phys/StdLoosePi02gg/Particles',
-                         'Phys/StdLooseResolvedPi0/Particles',
-                         'Phys/StdLooseMergedPi0/Particles']
+    vetoContainersPi0 = ['Phys/StdLoosePi02gee/Particles',
+                         'Phys/StdLoosePi024e/Particles']
 
     vetoTool = dtt.Jpsi.addTupleTool("TupleToolVeto")
     vetoTool.Particle = 'J/psi(1S)'
-- 
GitLab


From 65b8589c4c028c3a356a18b97eb024f74ec5dd8d Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Tue, 19 Sep 2023 16:22:53 +0200
Subject: [PATCH 4/7] add reference channel Bu2JpsiK MC production

---
 B2munuee/Bu2JpsiK2ee_MCoptions.py | 149 +++++++
 B2munuee/StrippingB23MuNu.py      | 627 ++++++++++++++++++++++++++++++
 B2munuee/info.yaml                |  30 +-
 3 files changed, 805 insertions(+), 1 deletion(-)
 create mode 100644 B2munuee/Bu2JpsiK2ee_MCoptions.py
 create mode 100644 B2munuee/StrippingB23MuNu.py

diff --git a/B2munuee/Bu2JpsiK2ee_MCoptions.py b/B2munuee/Bu2JpsiK2ee_MCoptions.py
new file mode 100644
index 0000000000..7b80b8d357
--- /dev/null
+++ b/B2munuee/Bu2JpsiK2ee_MCoptions.py
@@ -0,0 +1,149 @@
+from Configurables import DaVinci, DecayTreeTuple, GaudiSequencer
+from PhysSelPython.Wrappers import SelectionSequence
+from DecayTreeTuple.Configuration import setDescriptorTemplate
+
+from Configurables import FilterInTrees, CombineParticles, DiElectronMaker
+from PhysSelPython.Wrappers import Selection, AutomaticData, CombineSelection
+
+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,
+}
+
+STRIPPING_LINES = ['StrippingB23MuNu_MueeFakeLine']
+
+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()])
+
+# select muons from stripping
+muon_filter = FilterInTrees('Muons_MueeFake',
+                    Inputs=['/Event/Phys/B23MuNu_MueeFakeLine/Particles'],
+                    Code="('mu+' == ABSID)")
+updateDoD(muon_filter)
+
+# select electrons from stripping
+electrons_filter = FilterInTrees('Electrons_MueeFake',
+                    Inputs=['/Event/Phys/B23MuNu_MueeFakeLine/Particles'],
+                    Code="('e+' == ABSID)")
+updateDoD(electrons_filter)
+
+# run dieelctron maker
+dieLL = DiElectronMaker('DiElectronMaker_B23MuNu')
+dieLL.Particle = "J/psi(1S)"
+
+dieLL.ElectronInputs = [f'Phys/Electrons_MueeFake/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 = True
+
+Jpsi = Selection('DiElectron', Algorithm=dieLL)
+
+# make Mother particle
+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)
+
+muons = AutomaticData(f'Phys/Muons_MueeFake/Particles')
+
+B = CombineParticles('MakeMother',
+                    DecayDescriptors=['[B+ -> J/psi(1S) mu+]cc'],
+                    MotherCut=BMotherCut)
+B_sel = Selection('Sel_B', Algorithm=B,
+                    RequiredSelections=[Jpsi, muons])
+
+# make nTuple
+dtt = DecayTreeTuple('B2MuNuEE')
+dtt.setDescriptorTemplate(
+    '${B}[B+ -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${mu}mu+]CC')
+
+Seq = SelectionSequence('Sequence', TopSelection=B_sel)
+dtt.Inputs = Seq.outputLocations()
+
+_seq = GaudiSequencer('MySequencer')
+_seq.Members += [Seq.sequence(), dtt]
+DaVinci().appendToMainSequence([_seq])
+
+# now fill the tuples
+utils.DefaultToolList(dtt)
+utils.MCToolList(dtt)
+utils.TriggerToolList(dtt)
+utils.DefaultLoKi(dtt)
+utils.AddDOCA(dtt)
+utils.JpsiConstraints(dtt)
+utils.TupleToolConeIsolation(dtt)
+utils.TupleToolSubstitution(dtt)
+
+
+
+        
\ No newline at end of file
diff --git a/B2munuee/StrippingB23MuNu.py b/B2munuee/StrippingB23MuNu.py
new file mode 100644
index 0000000000..47886cd381
--- /dev/null
+++ b/B2munuee/StrippingB23MuNu.py
@@ -0,0 +1,627 @@
+###############################################################################
+# (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.                                       #
+###############################################################################
+
+__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)
+
+        RelInfoTools=[
+                { "Type" : "RelInfoMuonIDPlus",
+                    "Variables" : ["MU_BDT"],
+                    "DaughterLocations"  : {
+                    "[B+ -> ^l+ l+ [l-]CC ]CC" : "Muon1BDT",
+                    "[B+ -> l+ ^l+ [l-]CC ]CC" : "Muon2BDT",
+                    "[B+ -> l+ l+ ^[l-]CC ]CC" : "Muon3BDT",
+                    }
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_first',
+                    'Particles' : [0,1]
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_second',
+                    'Particles' : [1,2]
+                }
+        ]        
+
+
+        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],
+            RelatedInfoTools = RelInfoTools
+            )
+
+        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],
+            RelatedInfoTools = RelInfoTools
+            )
+
+        # specify leptons to identify the muon candidate
+        RelInfoTools=[
+                { "Type" : "RelInfoMuonIDPlus",
+                    "Variables" : ["MU_BDT"],
+                    "DaughterLocations"  : {
+                    "[B+ -> ^[e+]CC mu+ [mu-]CC ]CC" : "Muon1BDT",
+                    "[B+ -> [e+]CC ^mu+ [mu-]CC ]CC" : "Muon2BDT",
+                    "[B+ -> [e+]CC mu+ ^[mu-]CC ]CC" : "Muon3BDT"
+                    }
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_first',
+                    'Particles' : [0,1]
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_second',
+                    'Particles' : [1,2]
+                }
+        ]        
+
+        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],
+            RelatedInfoTools = RelInfoTools
+            )
+
+        # specify leptons to identify the muon candidate
+        RelInfoTools=[
+                { "Type" : "RelInfoMuonIDPlus",
+                    "Variables" : ["MU_BDT"],
+                    "DaughterLocations"  : {
+                    "[B+ -> ^[mu+]CC e+ [e-]CC ]CC" : "Muon1BDT",
+                    "[B+ -> [mu+]CC ^e+ [e-]CC ]CC" : "Muon2BDT",
+                    "[B+ -> [mu+]CC e+ ^[e-]CC ]CC" : "Muon3BDT"
+                    }
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_first',
+                    'Particles' : [0,1]
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_second',
+                    'Particles' : [1,2]
+                }
+        ]        
+
+
+        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],
+            RelatedInfoTools = RelInfoTools
+            )
+
+
+        RelInfoTools_fake=[
+                { "Type" : "RelInfoMuonIDPlus",
+                    "Variables" : ["MU_BDT"],
+                    "DaughterLocations"  : {
+                    "[B+ -> [J/psi(1S) -> ^l+ [l-]CC ]CC l+]CC" : "Muon1BDT",
+                    "[B+ -> [J/psi(1S) -> l+ ^[l-]CC ]CC l+]CC" : "Muon2BDT",
+                    "[B+ -> [J/psi(1S) -> l+ [l-]CC ]CC ^l+]CC" : "Muon3BDT",
+                    }
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_first',
+                    'Particles' : [1,2]
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_second',
+                    'Particles' : [2,3]
+                }
+        ]        
+
+
+        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],
+            RelatedInfoTools = RelInfoTools_fake
+            )
+
+        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],
+            RelatedInfoTools = RelInfoTools_fake
+            )
+
+        # specify leptons to identify the muon candidate
+        RelInfoTools_fake_mumue = RelInfoTools_fake
+        # RelInfoTools_fake_mumue[0] = { "Type" : "RelInfoMuonIDPlus",
+        #                     "Variables" : ["MU_BDT"],
+        #                     "DaughterLocations"  : {
+        #                     "[B+ -> [J/psi(1S) -> ^mu+ [mu-]CC ]CC e+]CC" : "Muon1BDT",
+        #                     "[B+ -> [J/psi(1S) -> mu+ ^[mu-]CC ]CC e+]CC" : "Muon2BDT",
+        #                     "[B+ -> [J/psi(1S) -> mu+ [mu-]CC ]CC ^e+]CC" : "Muon3BDT"}
+        #                     }
+        RelInfoTools_fake=[
+                { "Type" : "RelInfoMuonIDPlus",
+                    "Variables" : ["MU_BDT"],
+                    "DaughterLocations"  : {
+                    "[B+ -> [J/psi(1S) -> ^mu+ [mu-]CC ]CC e+]CC" : "Muon1BDT",
+                    "[B+ -> [J/psi(1S) -> mu+ ^[mu-]CC ]CC e+]CC" : "Muon2BDT",
+                    "[B+ -> [J/psi(1S) -> mu+ [mu-]CC ]CC ^e+]CC" : "Muon3BDT"
+                    }
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_first',
+                    'Particles' : [1,2]
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_second',
+                    'Particles' : [2,3]
+                }
+        ]        
+
+
+        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],
+            RelatedInfoTools = RelInfoTools_fake
+            )
+
+        # specify leptons to identify the muon candidate
+        RelInfoTools_fake=[
+                { "Type" : "RelInfoMuonIDPlus",
+                    "Variables" : ["MU_BDT"],
+                    "DaughterLocations"  : {
+                    "[B+ -> [J/psi(1S) -> ^e+ [e-]CC ]CC mu+]CC" : "Muon1BDT",
+                    "[B+ -> [J/psi(1S) -> e+ ^[e-]CC ]CC mu+]CC" : "Muon2BDT",
+                    "[B+ -> [J/psi(1S) -> e+ [e-]CC ]CC ^mu+]CC" : "Muon3BDT"
+                    }
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_first',
+                    'Particles' : [1,2]
+                },
+                {'Type' : 'RelInfoTrackIsolationBDT2',
+                    'Location' : 'TrackIsolationBDT2_second',
+                    'Particles' : [2,3]
+                }
+        ]        
+
+
+        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],
+            #RelatedInfoTools = RelInfoTools_fake
+            )
+
+
+        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/info.yaml b/B2munuee/info.yaml
index 4a349339ed..bed17f7904 100644
--- a/B2munuee/info.yaml
+++ b/B2munuee/info.yaml
@@ -15,7 +15,6 @@ defaults:
 {%- 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:
@@ -88,4 +87,33 @@ defaults:
     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
-- 
GitLab


From eabf2faf978a6378d1391b81404578287d8a0d4c Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Fri, 22 Sep 2023 10:38:08 +0200
Subject: [PATCH 5/7] more streamlined selection of JpsiKee

---
 B2munuee/B2Vub_MCoptions.py       |   3 +-
 B2munuee/Bc2munucc_MCoptions.py   |   3 +-
 B2munuee/Bu2JpsiK2ee_MCoptions.py | 154 ++------------------------
 B2munuee/Bu2munuee_MCoptions.py   |   3 +-
 B2munuee/my_selections.py         | 178 +++++++++++++++++++++++-------
 B2munuee/my_utils.py              |   9 --
 6 files changed, 150 insertions(+), 200 deletions(-)

diff --git a/B2munuee/B2Vub_MCoptions.py b/B2munuee/B2Vub_MCoptions.py
index e0288987eb..e5046506ae 100644
--- a/B2munuee/B2Vub_MCoptions.py
+++ b/B2munuee/B2Vub_MCoptions.py
@@ -8,4 +8,5 @@ from B2munuee import my_selections as selections
 trees = ['default','SameSign','pions','eta']
 isMC=True
 addJpsiConstraints=False
-selections.MySelection(trees, isMC, addJpsiConstraints)
\ No newline at end of file
+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
index f99d80384a..110e2a6c13 100644
--- a/B2munuee/Bc2munucc_MCoptions.py
+++ b/B2munuee/Bc2munucc_MCoptions.py
@@ -7,4 +7,5 @@ from B2munuee import my_selections as selections
 trees = ['default', 'SameSign']
 isMC = True
 addJpsiConstraints = True
-selections.MySelection(trees, isMC, addJpsiConstraints)
\ No newline at end of file
+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
index 7b80b8d357..b5edcef425 100644
--- a/B2munuee/Bu2JpsiK2ee_MCoptions.py
+++ b/B2munuee/Bu2JpsiK2ee_MCoptions.py
@@ -1,149 +1,9 @@
-from Configurables import DaVinci, DecayTreeTuple, GaudiSequencer
-from PhysSelPython.Wrappers import SelectionSequence
-from DecayTreeTuple.Configuration import setDescriptorTemplate
+from B2munuee import my_selections as selections
 
-from Configurables import FilterInTrees, CombineParticles, DiElectronMaker
-from PhysSelPython.Wrappers import Selection, AutomaticData, CombineSelection
+# B+ -> mu nu eta' (-> e e gamma)
 
-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,
-}
-
-STRIPPING_LINES = ['StrippingB23MuNu_MueeFakeLine']
-
-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()])
-
-# select muons from stripping
-muon_filter = FilterInTrees('Muons_MueeFake',
-                    Inputs=['/Event/Phys/B23MuNu_MueeFakeLine/Particles'],
-                    Code="('mu+' == ABSID)")
-updateDoD(muon_filter)
-
-# select electrons from stripping
-electrons_filter = FilterInTrees('Electrons_MueeFake',
-                    Inputs=['/Event/Phys/B23MuNu_MueeFakeLine/Particles'],
-                    Code="('e+' == ABSID)")
-updateDoD(electrons_filter)
-
-# run dieelctron maker
-dieLL = DiElectronMaker('DiElectronMaker_B23MuNu')
-dieLL.Particle = "J/psi(1S)"
-
-dieLL.ElectronInputs = [f'Phys/Electrons_MueeFake/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 = True
-
-Jpsi = Selection('DiElectron', Algorithm=dieLL)
-
-# make Mother particle
-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)
-
-muons = AutomaticData(f'Phys/Muons_MueeFake/Particles')
-
-B = CombineParticles('MakeMother',
-                    DecayDescriptors=['[B+ -> J/psi(1S) mu+]cc'],
-                    MotherCut=BMotherCut)
-B_sel = Selection('Sel_B', Algorithm=B,
-                    RequiredSelections=[Jpsi, muons])
-
-# make nTuple
-dtt = DecayTreeTuple('B2MuNuEE')
-dtt.setDescriptorTemplate(
-    '${B}[B+ -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${mu}mu+]CC')
-
-Seq = SelectionSequence('Sequence', TopSelection=B_sel)
-dtt.Inputs = Seq.outputLocations()
-
-_seq = GaudiSequencer('MySequencer')
-_seq.Members += [Seq.sequence(), dtt]
-DaVinci().appendToMainSequence([_seq])
-
-# now fill the tuples
-utils.DefaultToolList(dtt)
-utils.MCToolList(dtt)
-utils.TriggerToolList(dtt)
-utils.DefaultLoKi(dtt)
-utils.AddDOCA(dtt)
-utils.JpsiConstraints(dtt)
-utils.TupleToolConeIsolation(dtt)
-utils.TupleToolSubstitution(dtt)
-
-
-
-        
\ No newline at end of file
+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
index 042f16c834..4f12913a23 100644
--- a/B2munuee/Bu2munuee_MCoptions.py
+++ b/B2munuee/Bu2munuee_MCoptions.py
@@ -5,4 +5,5 @@ from B2munuee import my_selections as selections
 trees = ['default', 'SameSign']
 isMC = True
 addJpsiConstraints = False
-selections.MySelection(trees, isMC, addJpsiConstraints)
\ No newline at end of file
+run_stripping = False
+selections.MySelection(trees, isMC, run_stripping, addJpsiConstraints)
\ No newline at end of file
diff --git a/B2munuee/my_selections.py b/B2munuee/my_selections.py
index 1369a39db5..542216b5c1 100644
--- a/B2munuee/my_selections.py
+++ b/B2munuee/my_selections.py
@@ -3,13 +3,18 @@ from PhysSelPython.Wrappers import SelectionSequence
 from DecayTreeTuple.Configuration import *
 
 from Configurables import FilterInTrees, CombineParticles, DiElectronMaker
-from PhysSelPython.Wrappers import Selection, AutomaticData, CombineSelection
+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,
@@ -39,20 +44,55 @@ cuts = {
     "Eta_Jpsi_M_upper": 650.0 * MeV,
 }
 
+def RunB23MuNuStripping(B23MuNu_lines):
+    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()
 
-def RunDiElectronMaker(tree):
+    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
     '''
-    dieLL = DiElectronMaker('DiElectron-' + tree)
-    dieLL.Particle = "J/psi(1S)"
-
-    if tree in ['default', 'SameSign', 'pions', 'eta']:
-        dieLL.ElectronInputs = [f'Phys/Electrons_{tree}/Particles']
+    if oppositeSign:
+        dieLL = DiElectronMaker('DiElectron_' + line)
     else:
-        raise ValueError(
-            f'{tree} is not a valid option ro run DiElectronMaker')
+        dieLL = DiElectronMaker('DiElectron_' + line + '_SS')
+
+    dieLL.Particle = "J/psi(1S)"
+    dieLL.ElectronInputs = [f'Phys/Electrons_{line}/Particles']
 
     # some of these cuts do not differ from the default values
     # but are implemented to allow easy configuration
@@ -64,15 +104,9 @@ def RunDiElectronMaker(tree):
     dieLL.DiElectronPtMin = cuts['DiElectron_PT']
     dieLL.ElectronPtMin = cuts['Electron_PT']
     dieLL.AddBrem = True
+    dieLL.OppositeSign = oppositeSign
 
-    if tree in ['SameSign']:
-        dieLL.OppositeSign = False
-    else:
-        dieLL.OppositeSign = True
-
-    DiElectron = Selection('DiElectron_' + tree, Algorithm=dieLL)
-
-    return DiElectron
+    updateDoD(dieLL)
 
 def MakePion(Dielectron):
     '''
@@ -139,7 +173,7 @@ def MakeEta(Dielectron):
     return eta_sel
 
 
-def MakeMother(tree, Jpsi):
+def MakeMother(tree):
     '''
     Combine di-electon object and muon to a B candidate and apply default cuts
     '''
@@ -153,9 +187,10 @@ def MakeMother(tree, Jpsi):
         "(M > {LOWERMASS}) & "
         "(M < {UPPERMASS})").format(**cuts)
 
-    muons = AutomaticData(f'Phys/Muons_{tree}/Particles')
-
     if tree in ['pions']:
+        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine/Particles')
+
         pi0 = MakePion(Jpsi)
         B = CombineParticles('MakeMother_' + tree,
                              DecayDescriptor='[B+ -> pi0 mu+]cc',
@@ -163,6 +198,9 @@ def MakeMother(tree, Jpsi):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[pi0, muons])
     elif tree in ['eta']:
+        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine/Particles')
+
         eta = MakeEta(Jpsi)
         B = CombineParticles('MakeMother_' + tree,
                              DecayDescriptor='[B+ -> eta mu+]cc',
@@ -170,6 +208,9 @@ def MakeMother(tree, Jpsi):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[eta, muons])
     elif tree in ['SameSign']:
+        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine_SS/Particles')
+
         B = CombineParticles('MakeMother_' + tree,
                              DecayDescriptors=[
                                  '[B+ -> J/psi(1S) mu+]cc', '[B+ -> J/psi(1S) mu-]cc'],
@@ -177,6 +218,18 @@ def MakeMother(tree, Jpsi):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[Jpsi, muons])
     elif tree in ['default']:
+        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData(f'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(f'Phys/Muons_B23MuNu_MueeFakeLine/Particles')
+        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeFakeLine/Particles')
+        
         B = CombineParticles('MakeMother_' + tree,
                              DecayDescriptors=['[B+ -> J/psi(1S) mu+]cc'],
                              MotherCut=BMotherCut)
@@ -192,9 +245,10 @@ def MakeMother(tree, Jpsi):
 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']:
+    if name in ['default','JpsiKee']:
         dtt.setDescriptorTemplate(
             '${B}[B+ -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${mu}mu+]CC')
     elif name in ['SameSign']:
@@ -219,34 +273,74 @@ def MakeNTuples(B, name):
 
     return dtt
 
-def MySelection(trees, isMC, addJpsiConstraints):
-    if isMC:
-        stream = 'AllStreams'
+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 = 'Semileptonic'
+        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=[f'{stream}/B23MuNu_MueeLine/Particles'],
+                            Code="('mu+' == ABSID)")
+        updateDoD(muon_Muee)
+
+        # select electrons from default MueeLine
+        electrons_Muee = FilterInTrees('Electrons_B23MuNu_MueeLine',
+                            Inputs=[f'{stream}/B23MuNu_MueeLine/Particles'],
+                            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 not in ['default', 'SameSign', 'pions', 'eta']:
-            raise ValueError(
-                f'Selection for specified tree {tree} is not supported')
+        # if tree does not use MueeLine
+        if tree in ['JpsiKee']:
+            line = 'B23MuNu_MueeFakeLine'
 
-        # select muons from stripping or std container
-        muon_filter = FilterInTrees('Muons_'+tree,
-                            Inputs=[f'/Event/{stream}/Phys/B23MuNu_MueeLine/Particles'],
-                            Code="('mu+' == ABSID)")
-        updateDoD(muon_filter)
+            # select muons from stripping or std container
+            muon_filter = FilterInTrees('Muons_'+line,
+                                Inputs=[f'{stream}/{line}/Particles'],
+                                Code="('mu+' == ABSID)")
+            updateDoD(muon_filter)
 
-        # select electrons from stripping or std container
-        electrons_filter = FilterInTrees('Electrons_'+tree,
-                            Inputs=[f'/Event/{stream}/Phys/B23MuNu_MueeLine/Particles'],
-                            Code="('e+' == ABSID)")
-        updateDoD(electrons_filter)
+            # select electrons from stripping or std container
+            electrons_filter = FilterInTrees('Electrons_'+line,
+                                Inputs=[f'{stream}/{line}/Particles'],
+                                Code="('e+' == ABSID)")
+            updateDoD(electrons_filter)
+
+            # run dieelctron maker
+            RunDiElectronMaker('B23MuNu_MueeFakeLine')
 
-        # run dieelctron maker
-        DiLepton = RunDiElectronMaker(tree)
+        # if SameSign also need to run DiElectronMaker again
+        if tree == 'SameSign':
+            RunDiElectronMaker('B23MuNu_MueeLine', oppositeSign=False)
 
         # make Mother particle
-        mother = MakeMother(tree, DiLepton)
+        mother = MakeMother(tree)
 
         # make nTuple
         dtt = MakeNTuples(mother, tree)
@@ -257,6 +351,8 @@ def MySelection(trees, isMC, addJpsiConstraints):
             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)
diff --git a/B2munuee/my_utils.py b/B2munuee/my_utils.py
index fe69168d0b..dc2c402519 100644
--- a/B2munuee/my_utils.py
+++ b/B2munuee/my_utils.py
@@ -298,12 +298,3 @@ def TupleToolSubstitution(dtt):
     SubstTool = dtt.B.addTupleTool('TupleToolSubMass/B_SubMass')
     SubstTool.Substitution += substitutions
 
-def TupleToolVeto(dtt):
-    ''' Add tool for pi0 veto for single photons'''
-
-    vetoContainersPi0 = ['Phys/StdLoosePi02gee/Particles',
-                         'Phys/StdLoosePi024e/Particles']
-
-    vetoTool = dtt.Jpsi.addTupleTool("TupleToolVeto")
-    vetoTool.Particle = 'J/psi(1S)'
-    vetoTool.Veto['_Pi0'] = vetoContainersPi0
-- 
GitLab


From 7ebee8592b67ce61bc9b75f22b3bddaf66552e5d Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Fri, 22 Sep 2023 16:56:49 +0200
Subject: [PATCH 6/7] minor style changes

---
 B2munuee/my_selections.py | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/B2munuee/my_selections.py b/B2munuee/my_selections.py
index 542216b5c1..101d6a5961 100644
--- a/B2munuee/my_selections.py
+++ b/B2munuee/my_selections.py
@@ -92,7 +92,7 @@ def RunDiElectronMaker(line, oppositeSign=True):
         dieLL = DiElectronMaker('DiElectron_' + line + '_SS')
 
     dieLL.Particle = "J/psi(1S)"
-    dieLL.ElectronInputs = [f'Phys/Electrons_{line}/Particles']
+    dieLL.ElectronInputs = ['Phys/Electrons_'+line+'/Particles']
 
     # some of these cuts do not differ from the default values
     # but are implemented to allow easy configuration
@@ -188,8 +188,8 @@ def MakeMother(tree):
         "(M < {UPPERMASS})").format(**cuts)
 
     if tree in ['pions']:
-        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
-        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine/Particles')
+        muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine/Particles')
 
         pi0 = MakePion(Jpsi)
         B = CombineParticles('MakeMother_' + tree,
@@ -198,8 +198,8 @@ def MakeMother(tree):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[pi0, muons])
     elif tree in ['eta']:
-        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
-        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine/Particles')
+        muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine/Particles')
 
         eta = MakeEta(Jpsi)
         B = CombineParticles('MakeMother_' + tree,
@@ -208,8 +208,8 @@ def MakeMother(tree):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[eta, muons])
     elif tree in ['SameSign']:
-        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
-        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine_SS/Particles')
+        muons = AutomaticData('Phys/Muons_B23MuNu_MueeLine/Particles')
+        Jpsi = AutomaticData('Phys/DiElectron_B23MuNu_MueeLine_SS/Particles')
 
         B = CombineParticles('MakeMother_' + tree,
                              DecayDescriptors=[
@@ -218,8 +218,8 @@ def MakeMother(tree):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[Jpsi, muons])
     elif tree in ['default']:
-        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeLine/Particles')
-        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeLine/Particles')
+        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'],
@@ -227,8 +227,8 @@ def MakeMother(tree):
         B_sel = Selection('Sel_B_' + tree, Algorithm=B,
                           RequiredSelections=[Jpsi, muons])
     elif tree in ['JpsiKee']:
-        muons = AutomaticData(f'Phys/Muons_B23MuNu_MueeFakeLine/Particles')
-        Jpsi = AutomaticData(f'Phys/DiElectron_B23MuNu_MueeFakeLine/Particles')
+        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'],
@@ -237,7 +237,7 @@ def MakeMother(tree):
                           RequiredSelections=[Jpsi, muons])
     else:
         raise ValueError(
-            f'{tree} is no valid tree for MakeMother')
+            tree + ' is no valid tree for MakeMother')
 
     return B_sel
 
@@ -262,7 +262,7 @@ def MakeNTuples(B, name):
             '${B}[B+ -> ${eta}(eta -> ${Jpsi}(J/psi(1S) -> ${ep}e+ ${em}e-) ${gamma}gamma) ${mu}mu+]CC')
     else:
         raise ValueError(
-            f'{name} is not a valid option for MakeNTuples(B, name)')
+            name + ' is not a valid option for MakeNTuples(B, name)')
 
     Seq = SelectionSequence('Sequence_' + name, TopSelection=B)
     dtt.Inputs = Seq.outputLocations()
@@ -300,13 +300,13 @@ def MySelection(trees, isMC, run_stripping, addJpsiConstraints):
     if any(item in ['default','pions','eta','SameSign'] for item in trees):
         # select muons from default MueeLine
         muon_Muee = FilterInTrees('Muons_B23MuNu_MueeLine',
-                            Inputs=[f'{stream}/B23MuNu_MueeLine/Particles'],
+                            Inputs=['{}/B23MuNu_MueeLine/Particles'.format(stream)],
                             Code="('mu+' == ABSID)")
         updateDoD(muon_Muee)
 
         # select electrons from default MueeLine
         electrons_Muee = FilterInTrees('Electrons_B23MuNu_MueeLine',
-                            Inputs=[f'{stream}/B23MuNu_MueeLine/Particles'],
+                            Inputs=['{}/B23MuNu_MueeLine/Particles'.format(stream)],
                             Code="('e+' == ABSID)")
         updateDoD(electrons_Muee)
 
@@ -322,13 +322,13 @@ def MySelection(trees, isMC, run_stripping, addJpsiConstraints):
 
             # select muons from stripping or std container
             muon_filter = FilterInTrees('Muons_'+line,
-                                Inputs=[f'{stream}/{line}/Particles'],
+                                Inputs=['{}/{}/Particles'.format(stream, line)],
                                 Code="('mu+' == ABSID)")
             updateDoD(muon_filter)
 
             # select electrons from stripping or std container
             electrons_filter = FilterInTrees('Electrons_'+line,
-                                Inputs=[f'{stream}/{line}/Particles'],
+                                Inputs=['{}/{}/Particles'.format(stream, line)],
                                 Code="('e+' == ABSID)")
             updateDoD(electrons_filter)
 
-- 
GitLab


From c6f7ac0a777d2c26d99458da81e72da6fbf87191 Mon Sep 17 00:00:00 2001
From: Fabian Christoph Glaser <fabian.christoph.glaser@cern.ch>
Date: Mon, 25 Sep 2023 09:44:52 +0200
Subject: [PATCH 7/7] Update documentation and change to DaVinci v46r7

---
 B2munuee/StrippingB23MuNu.py | 171 ++++-------------------------------
 B2munuee/info.yaml           |   2 +-
 B2munuee/my_selections.py    |   6 ++
 3 files changed, 27 insertions(+), 152 deletions(-)

diff --git a/B2munuee/StrippingB23MuNu.py b/B2munuee/StrippingB23MuNu.py
index 47886cd381..858afff4b3 100644
--- a/B2munuee/StrippingB23MuNu.py
+++ b/B2munuee/StrippingB23MuNu.py
@@ -9,6 +9,15 @@
 # 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 $'
@@ -126,26 +135,6 @@ class B23MuNuConf(LineBuilder) :
         self.FakeMuMue = self.__FakeMuMue__(config)
         self.FakeMuee = self.__FakeMuee__(config)
 
-        RelInfoTools=[
-                { "Type" : "RelInfoMuonIDPlus",
-                    "Variables" : ["MU_BDT"],
-                    "DaughterLocations"  : {
-                    "[B+ -> ^l+ l+ [l-]CC ]CC" : "Muon1BDT",
-                    "[B+ -> l+ ^l+ [l-]CC ]CC" : "Muon2BDT",
-                    "[B+ -> l+ l+ ^[l-]CC ]CC" : "Muon3BDT",
-                    }
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_first',
-                    'Particles' : [0,1]
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_second',
-                    'Particles' : [1,2]
-                }
-        ]        
-
-
         self.TriMu_line =  StrippingLine(
             self.name+"_TriMuLine",
             prescale = 1,
@@ -155,8 +144,7 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.Trimu],
-            RelatedInfoTools = RelInfoTools
+            algos=[self.Trimu]
             )
 
         self.Trie_line =  StrippingLine(
@@ -168,29 +156,8 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.Trie],
-            RelatedInfoTools = RelInfoTools
-            )
-
-        # specify leptons to identify the muon candidate
-        RelInfoTools=[
-                { "Type" : "RelInfoMuonIDPlus",
-                    "Variables" : ["MU_BDT"],
-                    "DaughterLocations"  : {
-                    "[B+ -> ^[e+]CC mu+ [mu-]CC ]CC" : "Muon1BDT",
-                    "[B+ -> [e+]CC ^mu+ [mu-]CC ]CC" : "Muon2BDT",
-                    "[B+ -> [e+]CC mu+ ^[mu-]CC ]CC" : "Muon3BDT"
-                    }
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_first',
-                    'Particles' : [0,1]
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_second',
-                    'Particles' : [1,2]
-                }
-        ]        
+            algos=[self.Trie]
+            )       
 
         self.MuMue_line =  StrippingLine(
             self.name+"_MuMueLine",
@@ -201,30 +168,8 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.MuMue],
-            RelatedInfoTools = RelInfoTools
-            )
-
-        # specify leptons to identify the muon candidate
-        RelInfoTools=[
-                { "Type" : "RelInfoMuonIDPlus",
-                    "Variables" : ["MU_BDT"],
-                    "DaughterLocations"  : {
-                    "[B+ -> ^[mu+]CC e+ [e-]CC ]CC" : "Muon1BDT",
-                    "[B+ -> [mu+]CC ^e+ [e-]CC ]CC" : "Muon2BDT",
-                    "[B+ -> [mu+]CC e+ ^[e-]CC ]CC" : "Muon3BDT"
-                    }
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_first',
-                    'Particles' : [0,1]
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_second',
-                    'Particles' : [1,2]
-                }
-        ]        
-
+            algos=[self.MuMue]
+            ) 
 
         self.Muee_line =  StrippingLine(
             self.name+"_MueeLine",
@@ -235,30 +180,8 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.Muee],
-            RelatedInfoTools = RelInfoTools
-            )
-
-
-        RelInfoTools_fake=[
-                { "Type" : "RelInfoMuonIDPlus",
-                    "Variables" : ["MU_BDT"],
-                    "DaughterLocations"  : {
-                    "[B+ -> [J/psi(1S) -> ^l+ [l-]CC ]CC l+]CC" : "Muon1BDT",
-                    "[B+ -> [J/psi(1S) -> l+ ^[l-]CC ]CC l+]CC" : "Muon2BDT",
-                    "[B+ -> [J/psi(1S) -> l+ [l-]CC ]CC ^l+]CC" : "Muon3BDT",
-                    }
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_first',
-                    'Particles' : [1,2]
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_second',
-                    'Particles' : [2,3]
-                }
-        ]        
-
+            algos=[self.Muee]
+            )      
 
         self.FakeTriMu_line =  StrippingLine(
             self.name+"_TriFakeMuLine",
@@ -269,8 +192,7 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.FakeTrimu],
-            RelatedInfoTools = RelInfoTools_fake
+            algos=[self.FakeTrimu]
             )
 
         self.FakeTrie_line =  StrippingLine(
@@ -282,39 +204,9 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.FakeTrie],
-            RelatedInfoTools = RelInfoTools_fake
+            algos=[self.FakeTrie]
             )
 
-        # specify leptons to identify the muon candidate
-        RelInfoTools_fake_mumue = RelInfoTools_fake
-        # RelInfoTools_fake_mumue[0] = { "Type" : "RelInfoMuonIDPlus",
-        #                     "Variables" : ["MU_BDT"],
-        #                     "DaughterLocations"  : {
-        #                     "[B+ -> [J/psi(1S) -> ^mu+ [mu-]CC ]CC e+]CC" : "Muon1BDT",
-        #                     "[B+ -> [J/psi(1S) -> mu+ ^[mu-]CC ]CC e+]CC" : "Muon2BDT",
-        #                     "[B+ -> [J/psi(1S) -> mu+ [mu-]CC ]CC ^e+]CC" : "Muon3BDT"}
-        #                     }
-        RelInfoTools_fake=[
-                { "Type" : "RelInfoMuonIDPlus",
-                    "Variables" : ["MU_BDT"],
-                    "DaughterLocations"  : {
-                    "[B+ -> [J/psi(1S) -> ^mu+ [mu-]CC ]CC e+]CC" : "Muon1BDT",
-                    "[B+ -> [J/psi(1S) -> mu+ ^[mu-]CC ]CC e+]CC" : "Muon2BDT",
-                    "[B+ -> [J/psi(1S) -> mu+ [mu-]CC ]CC ^e+]CC" : "Muon3BDT"
-                    }
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_first',
-                    'Particles' : [1,2]
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_second',
-                    'Particles' : [2,3]
-                }
-        ]        
-
-
         self.FakeMuMue_line =  StrippingLine(
             self.name+"_MuMueFakeLine",
             prescale = config['MisIDPrescale'],
@@ -324,31 +216,9 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.FakeMuMue],
-            RelatedInfoTools = RelInfoTools_fake
+            algos=[self.FakeMuMue]
             )
 
-        # specify leptons to identify the muon candidate
-        RelInfoTools_fake=[
-                { "Type" : "RelInfoMuonIDPlus",
-                    "Variables" : ["MU_BDT"],
-                    "DaughterLocations"  : {
-                    "[B+ -> [J/psi(1S) -> ^e+ [e-]CC ]CC mu+]CC" : "Muon1BDT",
-                    "[B+ -> [J/psi(1S) -> e+ ^[e-]CC ]CC mu+]CC" : "Muon2BDT",
-                    "[B+ -> [J/psi(1S) -> e+ [e-]CC ]CC ^mu+]CC" : "Muon3BDT"
-                    }
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_first',
-                    'Particles' : [1,2]
-                },
-                {'Type' : 'RelInfoTrackIsolationBDT2',
-                    'Location' : 'TrackIsolationBDT2_second',
-                    'Particles' : [2,3]
-                }
-        ]        
-
-
         self.FakeMuee_line =  StrippingLine(
             self.name+"_MueeFakeLine",
             prescale = config['MisIDPrescale'],
@@ -358,8 +228,7 @@ class B23MuNuConf(LineBuilder) :
             "from LoKiNumbers.decorators import *", "from LoKiCore.basic import LHCb"
             ]
             },
-            algos=[self.FakeMuee],
-            #RelatedInfoTools = RelInfoTools_fake
+            algos=[self.FakeMuee]
             )
 
 
diff --git a/B2munuee/info.yaml b/B2munuee/info.yaml
index bed17f7904..80e4ed68c1 100644
--- a/B2munuee/info.yaml
+++ b/B2munuee/info.yaml
@@ -1,5 +1,5 @@
 defaults:
-    application: DaVinci/v46r4
+    application: DaVinci/v46r7
     wg: SL
     automatically_configure: yes
     turbo: no
diff --git a/B2munuee/my_selections.py b/B2munuee/my_selections.py
index 101d6a5961..70213828ef 100644
--- a/B2munuee/my_selections.py
+++ b/B2munuee/my_selections.py
@@ -45,6 +45,12 @@ cuts = {
 }
 
 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 = {
-- 
GitLab