From f48bbdb5c694974f16e784e9ffeb8509d428ac83 Mon Sep 17 00:00:00 2001
From: yajing <yajing.wei@cern.ch>
Date: Tue, 26 Nov 2024 02:23:50 +0000
Subject: [PATCH 1/6] Initial commit.

---
 bandq_resprucing/B2psiX/dv_data.py            |  65 ++
 bandq_resprucing/B2psiX/template.py           | 167 +++
 .../B2psiX/tools/descriptor_writer.py         | 153 +++
 .../B2psiX/tools/tupling_maker.py             | 387 +++++++
 bandq_resprucing/BforSpectroscopy/dv_data.py  | 949 ++++++++++++++++++
 bandq_resprucing/BforSpectroscopy/make_dtf.py | 228 +++++
 .../BforSpectroscopy/tupling_maker.py         | 345 +++++++
 .../dv_data_double_charmonia_OS.py            | 123 +++
 .../dv_data_double_charmonia_SS.py            | 130 +++
 bandq_resprucing/double_charm/tupling.py      | 267 +++++
 bandq_resprucing/info.yaml                    |  35 +
 bandq_resprucing/tupling.py                   |  33 +
 12 files changed, 2882 insertions(+)
 create mode 100644 bandq_resprucing/B2psiX/dv_data.py
 create mode 100644 bandq_resprucing/B2psiX/template.py
 create mode 100644 bandq_resprucing/B2psiX/tools/descriptor_writer.py
 create mode 100644 bandq_resprucing/B2psiX/tools/tupling_maker.py
 create mode 100644 bandq_resprucing/BforSpectroscopy/dv_data.py
 create mode 100644 bandq_resprucing/BforSpectroscopy/make_dtf.py
 create mode 100644 bandq_resprucing/BforSpectroscopy/tupling_maker.py
 create mode 100644 bandq_resprucing/double_charm/dv_data_double_charmonia_OS.py
 create mode 100644 bandq_resprucing/double_charm/dv_data_double_charmonia_SS.py
 create mode 100644 bandq_resprucing/double_charm/tupling.py
 create mode 100644 bandq_resprucing/info.yaml
 create mode 100644 bandq_resprucing/tupling.py

diff --git a/bandq_resprucing/B2psiX/dv_data.py b/bandq_resprucing/B2psiX/dv_data.py
new file mode 100644
index 0000000000..38bf0f5177
--- /dev/null
+++ b/bandq_resprucing/B2psiX/dv_data.py
@@ -0,0 +1,65 @@
+from .template import *
+
+def Lb2JpsipK():
+    algs = template(["Lambda_b0", "J/psi(1S)", "p+", "K-"], True, "Lb2JpsipK")
+    #  return make_config( options, algs )
+    return algs
+
+def Lb2Jpsippi():
+    algs = template(["Lambda_b0", "J/psi(1S)", "p+", "pi-"], True, "Lb2Jpsippi")
+    #  return make_config( options, algs )
+    return algs
+
+def B2JpsiKpi():
+    algs = template(["B0", "J/psi(1S)", "K+", "pi-"], True, "B2JpsiKpi")
+    #  return make_config( options, algs )
+    return algs
+
+def Bp2JpsiphiK():
+    algs = template(["B+", "J/psi(1S)", "phi(1020)", "K+"], True, "Bp2JpsiphiK")
+#      return make_config( options, algs )
+    return algs
+
+def B2Jpsippbar():
+    algs = template(["B_s0", "J/psi(1S)", "p+", "p~-"], False, "B2Jpsippbar")
+#      return make_config( options, algs )
+    return algs
+
+def Xib2JpsiLambdaK_LL():
+    algs = template(["Xi_b-", "J/psi(1S)", "Lambda0", "K-"], True, "Xib2JpsiLambdaK_LL") #LL 
+    return algs
+
+def Xib2JpsiLambdaK_DD():
+    algs = template(["Xi_b-", "J/psi(1S)", "Lambda0_DD", "K-"], True, "Xib2JpsiLambdaK_DD") #DD
+    return algs
+
+def Bp2JpsipLambdabar_LL():
+    algs = template(["B+", "J/psi(1S)", "p+", "Lambda~0"], True, "Bp2JpsipLambdabar_LL")
+    return algs
+
+def Bp2JpsipLambdabar_DD():
+    algs = template(["B+", "J/psi(1S)", "p+", "Lambda~0_DD"], True, "Bp2JpsipLambdabar_DD")
+    return algs
+
+def Bp2JpsiKS0pi_LL():
+    algs = template(["B+", "J/psi(1S)", "KS0", "pi+"], True, "Bp2JpsiKS0pi_LL")
+    return algs
+
+def Bp2JpsiKS0pi_DD():
+    algs = template(["B+", "J/psi(1S)", "KS0_DD", "pi+"], True, "Bp2JpsiKS0pi_DD")
+    return algs
+
+# def entry_point(options: Options):
+#     algs_merged = Lb2JpsipK()
+#     algs_merged.update(Lb2Jpsippi())
+#     algs_merged.update(B2JpsiKpi())
+#     algs_merged.update(Bp2JpsiphiK())
+#     algs_merged.update(B2Jpsippbar())
+#     algs_merged.update(Xib2JpsiLambdaK_LL())
+#     algs_merged.update(Xib2JpsiLambdaK_DD())
+#     algs_merged.update(Bp2JpsipLambdabar_LL())
+#     algs_merged.update(Bp2JpsipLambdabar_DD())
+#     algs_merged.update(Bp2JpsiKS0pi_LL())
+#     algs_merged.update(Bp2JpsiKS0pi_DD())
+
+#     return make_config(options, algs_merged)
diff --git a/bandq_resprucing/B2psiX/template.py b/bandq_resprucing/B2psiX/template.py
new file mode 100644
index 0000000000..993c0b94e5
--- /dev/null
+++ b/bandq_resprucing/B2psiX/template.py
@@ -0,0 +1,167 @@
+from .tools.tupling_maker import *
+from .tools.descriptor_writer import *
+
+from GaudiKernel.SystemOfUnits import GeV, MeV, mm, meter, picosecond
+
+from RecoConf.reconstruction_objects import make_pvs as _make_pvs
+
+from Hlt2Conf.standard_particles import make_long_kaons, make_long_protons, make_long_pions, make_LambdaLL, make_long_muons, make_phi2kk, make_down_pions, make_down_protons 
+from Hlt2Conf.standard_particles import make_LambdaDD, make_KsDD, make_KsLL, _make_particles, _make_V0DD, standard_protoparticle_filter 
+from Hlt2Conf.algorithms_thor import ParticleFilter
+
+import Functors as F
+
+masses = {
+    'pi0': 134.9768 *
+    MeV,  # +/- 0.0005 PDG, P.A. Zyla et al. (Particle Data Group), Prog. Theor. Exp. Phys. 2020, 083C01 (2020) and 2021 update.
+    'KS0': 497.611 * MeV,  # +/- 0.013, PDG, PR D98, 030001 and 2019 update
+    'Lambda0':
+    1115.683 * MeV,  # +/- 0.006, PDG, PR D98, 030001 and 2019 update
+    'J/psi(1S)': 3096.900 *
+    MeV,  # +/- 0.006 PDG, P.A. Zyla et al. (Particle Data Group), Prog. Theor. Exp. Phys. 2020, 083C01 (2020) and 2021 update.
+}
+
+def _make_down_for_V0_loose(particles):
+    code = F.require_all(F.P > 1500 * MeV, F.PT > 50 * MeV)
+    return ParticleFilter(particles, F.FILTER(code))
+
+def make_down_pions_for_V0_loose():
+    return _make_down_for_V0_loose(make_down_pions())
+
+def make_down_protons_for_V0_loose():
+    return _make_down_for_V0_loose(make_down_protons())
+
+def make_KsDD_loose(pions_down=None):
+    if pions_down is None:
+        pions_down = make_down_pions_for_V0_loose()
+    descriptors = "KS0 -> pi+ pi-"
+    return _make_V0DD(
+        particles=[pions_down,pions_down],
+        descriptors=descriptors,
+        pv_maker=_make_pvs,
+        name='std_make_V0DD_loose_{hash}',
+        bpvvdz_min=100 * mm)
+
+def make_LambdaDD_loose():
+    pions = make_down_pions_for_V0_loose()
+    protons = make_down_protons_for_V0_loose()
+    descriptors = "[Lambda0 -> p+ pi-]cc"
+    return _make_V0DD(
+        particles=[protons, pions],
+        descriptors=descriptors,
+        pv_maker=_make_pvs,
+        name='std_make_V0DD_loose_{hash}',
+        nominal_mass=masses['Lambda0'],
+        am_dmass=80 * MeV,
+        m_dmass=24 * MeV,
+        vchi2pdof_max=30,
+        bpvvdz_min=100 * mm)
+
+def template(particles, cc, directory):
+
+    ##### Default inputs
+    sprucing_line = 'SpruceBandQ_JpsiToMuMuDetached'
+    Jpsi = get_particles(f"/Event/Spruce/{sprucing_line}/Particles")
+
+    rec_sum = get_rec_summary()
+    v2_pvs = get_pvs()
+    odin = get_odin()
+    decreports = None
+    
+    long_pions = make_long_pions()
+    long_kaons = make_long_kaons()
+    long_protons = make_long_protons()
+    
+    DaughterCutp = "(BPVIPCHI2(get_pvs())>9) & (PT>250*MeV) & (P>10*GeV) & (PID_P>5) & (PID_P-PID_K>5) & (GHOSTPROB<0.5)"
+    DaughterCutpi = "(BPVIPCHI2(get_pvs())>9) & (PT>250*MeV) & (PID_K<5) & (GHOSTPROB<0.5)"
+    DaughterCutK = "(BPVIPCHI2(get_pvs())>9) & (PT>250*MeV) & (PID_K>5) & (GHOSTPROB<0.5)"
+    
+    protons = ParticleFilter( long_protons, Cut = F.FILTER( convert_cut(DaughterCutp) ) )
+    kaons = ParticleFilter( long_kaons, Cut = F.FILTER( convert_cut(DaughterCutK) ) )
+    pions = ParticleFilter( long_pions, Cut = F.FILTER( convert_cut(DaughterCutpi) ) )
+    muons = make_long_muons()
+    Lambda = make_LambdaLL()
+    Lambda_DD = make_LambdaDD_loose()
+    KS0 = make_KsLL()
+    KS0_DD = make_KsDD_loose()
+    phi = make_phi2kk(kaon_pidk_min=5)
+
+    input_dict = {"J/psi(1S)": Jpsi, "phi(1020)": phi,
+                  "Lambda0":Lambda, "Lambda~0":Lambda,
+                  "pi+": pions, "pi-": pions,
+                  "K+": kaons, "K-": kaons,
+                  "p+": protons, "p~-": protons,
+                  "mu+": muons, "mu~-": muons,
+                  "Lambda0_DD":Lambda_DD, "Lambda~0_DD":Lambda_DD,
+                  "KS0":KS0, "KS0_DD":KS0_DD,
+                 }
+
+    data_list = [input_dict[particle] for particle in particles[1:]]
+    # create particle containers
+
+    for i in range(len(particles)):
+        if particles[i][-3:] == "_DD":
+            particles[i] = particles[i][:-3]
+            #print(particles[i])
+
+    B2JpsiX = ParticleCombiner(
+            Inputs = data_list,
+            DecayDescriptor = descriptor(particles, cc),
+            name = "JpsiToMuMu_Detached_line_validation_{hash}",
+            CombinationCut = convert_cut("in_range(4000, MASS, 7000) & (CHILD(2, PT)+CHILD(3, PT)>900*MeV)"),
+            #CombinationCut = convert_cut("in_range(5200, MASS, 7000)"),
+            CompositeCut = convert_cut("in_range(4000, MASS, 7000) & CHI2DOF<20 & BPVLTIME(get_pvs())>0.2*picosecond & BPVVDZ(get_pvs())>0.*mm")
+    )
+
+    from pprint import pprint
+    print("++++++++++++++++++++++++++++++++++++")
+    pprint(descriptor(particles, cc))
+    #FunTuple: define branches.
+
+    hlt1_trigger_lines = [ 'Hlt1TrackMVADecision',
+                           'Hlt1TwoTrackMVADecision',
+                           'Hlt1D2KKDecision',
+                           'Hlt1D2KPiDecision', 
+                           'Hlt1D2PiPiDecision',
+                           'Hlt1DiMuonHighMassDecision', 
+                           'Hlt1DiMuonLowMassDecision',
+                           'Hlt1DiMuonSoftDecision',
+                           'Hlt1KsToPiPiDecision',
+                           'Hlt1LowPtMuonDecision',
+                           'Hlt1LowPtDiMuonDecision',
+                           'Hlt1SingleHighPtMuonDecision',
+                           'Hlt1TrackMuonMVADecision']
+
+
+    line_prefilter = create_lines_filter(name=f"PreFilter_{sprucing_line}_{hash}", lines=[sprucing_line])
+    evt_vars = event_variables(v2_pvs, odin, decreports, rec_sum, hlt1_trigger_lines, [sprucing_line])
+    candidate_vars = candidate_variables(v2_pvs, particles)
+    tistos_vars = tistos_variables(hlt1_trigger_lines, ["Hlt2Topo2BodyDecision", "Hlt2Topo3BodyDecision", "Hlt2_JpsiToMuMuDetachedFullDecision", "Hlt2_Psi2SToMuMuDetachedFull"], Jpsi, False)
+
+    composite_particles = [ particle for particle in particles if (particle_df["type"][particle] != "basic") and (particle != "phi(1020)") ]
+    dtf_vars1 = make_dtf_variables(v2_pvs, B2JpsiX, particles, True, composite_particles, directory)
+    dtf_vars2 = make_dtf_variables(v2_pvs, B2JpsiX, particles, True, composite_particles[1:], directory)
+
+    for particle in particles:
+        if particle_df["type"][particle] != "basic":
+            candidate_vars[particle_df["abbr"][particle]] += tistos_vars
+
+    for key in candidate_vars.keys():
+        candidate_vars[key] += dtf_vars1[key]
+        candidate_vars[key] += dtf_vars2[key]
+
+    B2JpsiX_branches = default_branches(particles, cc)
+    pprint(B2JpsiX_branches)
+
+    # define tupling algorithms
+    B2JpsiX_tuple = FunTuple_Particles( name=directory,
+                                         inputs=B2JpsiX,
+                                         tuple_name="DecayTree",
+                                         fields=B2JpsiX_branches,
+                                         variables=candidate_vars,
+                                         store_multiple_cand_info = True,
+                                         event_variables = evt_vars)
+
+    algs = { f"{directory}_tuple":[ B2JpsiX_tuple, line_prefilter ],}
+            
+    return algs
diff --git a/bandq_resprucing/B2psiX/tools/descriptor_writer.py b/bandq_resprucing/B2psiX/tools/descriptor_writer.py
new file mode 100644
index 0000000000..cfbd2acc90
--- /dev/null
+++ b/bandq_resprucing/B2psiX/tools/descriptor_writer.py
@@ -0,0 +1,153 @@
+import pandas as pd
+import numpy as np
+particle_dict = {"Lambda_b0": {"abbr": "Lb", "type": "toplevel", "daughters":[]},
+                 "Xi_b-":     {"abbr": "Xibm", "type": "toplevel", "daughters":[]},
+                 "Xi_b0":     {"abbr": "Xib0", "type": "toplevel", "daughters":[]},
+                 "B0":        {"abbr": "B0", "type": "toplevel", "daughters":[]},
+                 "B+":        {"abbr": "Bp", "type": "toplevel", "daughters":[]},
+                 "B-":        {"abbr": "Bm", "type": "toplevel", "daughters":[]},
+                 "B_s0":      {"abbr": "Bs", "type": "toplevel", "daughters":[]},
+                 "J/psi(1S)": {"abbr": "Jpsi", "type": "composite", "daughters": ["mu+", "mu-"]},
+                 "psi(2S)":   {"abbr": "psi2S", "type": "composite", "daughters": ["mu+", "mu-"]},
+                 "phi(1020)": {"abbr": "phi", "type": "composite", "daughters": ["K+", "K-"]},
+                 "Lambda0":   {"abbr": "Lambda", "type": "composite", "daughters": ["p+", "pi-"]},
+                 "Lambda~0":  {"abbr": "Lambda", "type": "composite", "daughters": ["p~-", "pi+"]},
+                 "KS0":       {"abbr": "KS0", "type": "composite", "daughters": ["pi+", "pi-"]},
+                 "mu+":       {"abbr": "mup", "type": "basic", "daughters":[]},
+                 "mu-":       {"abbr": "mum", "type": "basic", "daughters":[]},
+                 "e+":        {"abbr": "ep", "type": "basic", "daughters":[]},
+                 "e-":        {"abbr": "em", "type": "basic", "daughters":[]},
+                 "pi+":       {"abbr": "Pip", "type": "basic", "daughters":[]},
+                 "pi-":       {"abbr": "Pim", "type": "basic", "daughters":[]},
+                 "K+":        {"abbr": "Kp", "type": "basic", "daughters":[]},
+                 "K-":        {"abbr": "Km", "type": "basic", "daughters":[]},
+                 "p+":        {"abbr": "Pp", "type": "basic", "daughters":[]},
+                 "p~-":       {"abbr": "Pm", "type": "basic", "daughters":[]},
+            }
+particle_df = pd.DataFrame(particle_dict).T
+
+def descriptor(particles, cc=False):
+    decay_descriptor = f"{particles[0]} ->"
+    for particle in particles[1:]:
+        decay_descriptor += f" {particle}"
+    if cc: decay_descriptor = f"[{decay_descriptor}]cc"
+    return decay_descriptor
+
+def decay_branches(mother, daughters, decay_descriptor):
+    psi_pos = 0
+    phi_pos = 0
+    Lambda_pos = 0
+    KS0_pos = 0
+    Pm_pos = Pp_pos = 0
+    Km_pos = Kp_pos = 0
+    Pim_pos = Pip_pos = 0
+    mum_pos = mup_pos = 0
+    em_pos = ep_pos = 0
+
+    #if decay_descriptor[0] == "[" or decay_descriptor[-3:] == "]cc":
+    if "cc" in decay_descriptor[-5:]:
+        branch_descriptor = decay_descriptor[:-2] + "CC"
+    else: branch_descriptor = decay_descriptor
+    #if "J/psi(1S)" in decay_descriptor: 
+    #    branch_descriptor = branch_descriptor.replace("J/psi(1S)", "(J/psi(1S) -> mu+ mu-)")
+    #if "psi(2S)" in decay_descriptor: 
+    #    branch_descriptor = branch_descriptor.replace("psi(2S)", "(psi(2S) -> mu+ mu-)")
+    #if "Lambda0" in decay_descriptor: 
+    #    branch_descriptor = branch_descriptor.replace("Lambda0", "(Lambda0 -> p+ pi-)")
+    #if "Lambda~0" in decay_descriptor: 
+    #    branch_descriptor = branch_descriptor.replace("Lambda~0", "(Lambda~0 -> p~- pi+)")
+    for comp_par in particle_df.query("type=='composite'").index:
+        if comp_par in decay_descriptor:
+            #branch_descriptor = branch_descriptor.replace(comp_par, comp_par + "->" + " ".join(particle_df["daughters"][comp_par]))
+            branch_descriptor = branch_descriptor.replace(comp_par, f'({comp_par} -> {" ".join(particle_df["daughters"][comp_par])})')
+
+    branches = {mother: branch_descriptor}
+
+    for daughter in daughters:
+        if "psi" in daughter:
+            true_pos = branch_descriptor.rfind("(", 0, branch_descriptor.find("psi", psi_pos))
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            psi_pos = branch_descriptor.find("psi", psi_pos) + len("psi(nS)")
+        if "phi" in daughter:
+            true_pos = branch_descriptor.rfind("(", 0, branch_descriptor.find("phi", phi_pos))
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            phi_pos = branch_descriptor.find("phi", phi_pos) + len("phi(1020)")
+        if "Lambda" in daughter:
+            true_pos = branch_descriptor.rfind("(", 0, branch_descriptor.find("Lambda", Lambda_pos))
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Lambda_pos = branch_descriptor.find("Lambda", Lambda_pos) + len("Lambda~0")
+        if "KS0" in daughter:
+            true_pos = branch_descriptor.rfind("(", 0, branch_descriptor.find("KS0", KS0_pos))
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            KS0_pos = branch_descriptor.find("KS0", KS0_pos) + len("KS0")
+        if "Pp" in daughter:
+            true_pos = branch_descriptor.find("p+", Pp_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Pp_pos = true_pos + len("p+")
+        if "Pm" in daughter:
+            true_pos = branch_descriptor.find("p~-", Pm_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Pm_pos = true_pos + len("p~-")
+        if "Kp" in daughter:
+            true_pos = branch_descriptor.find("K+", Kp_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Kp_pos = true_pos + len("K+")
+        if "Km" in daughter:
+            true_pos = branch_descriptor.find("K-", Km_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Km_pos = true_pos + len("K-")
+        if "Pip" in daughter or "Hp" in daughter:
+            true_pos = branch_descriptor.find("pi+", Pip_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Pip_pos = true_pos + len("pi+")
+        if "Pim" in daughter or "Hm" in daughter:
+            true_pos = branch_descriptor.find("pi-", Pim_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            Pim_pos = true_pos + len("pi-")
+        if "mup" in daughter:
+            true_pos = branch_descriptor.find("mu+", mup_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            mup_pos = true_pos + len("mu+")
+        if "mum" in daughter:
+            true_pos = branch_descriptor.find("mu-", mum_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            mum_pos = true_pos + len("mu-")
+        if "ep" in daughter:
+            true_pos = branch_descriptor.find("e+", ep_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            ep_pos = true_pos + len("e+")
+        if "em" in daughter:
+            true_pos = branch_descriptor.find("e-", em_pos)
+            branches.update({daughter: branch_descriptor[:true_pos]+"^"+branch_descriptor[true_pos:]})
+            em_pos = true_pos + len("e-")
+    return branches
+
+def all_particles(particles):
+    all_pars = []
+    for particle in particles:
+        all_pars += ( [ particle ] + particle_df["daughters"][particle])
+    return all_pars
+
+#print(all_particles(["B_s0","J/psi(1S)", "p+", "p~-"]))
+
+def default_names(particles):
+    names = []
+    for particle in particles:
+        abbr = particle_df["abbr"][ particle ]
+        names.append( abbr )
+        names += [f"{daughter_abbr}_{abbr}" for daughter_abbr in particle_df["abbr"][particle_df["daughters"][particle]]]
+    return names
+
+def default_branches(particles, cc=False):
+    names = default_names(particles)
+    return decay_branches(names[0], names[1:], descriptor(particles, cc))
+
+#from pprint import pprint
+#branches = default_branches( ["B+","J/psi(1S)", "KS0", "pi+"], True)
+#pprint(branches)
+
+#branches = decay_branches("Bs", ["Jpsi", "Pp", "Pm", "mup", "mum"], "[B_s0 -> J/psi(1S) p+ p~-]cc")
+#print(branches)
+
+#branches = default_names( ["B_s0","J/psi(1S)", "p+", "p~-"])
+#print(descriptor(["Lambda_b0", "J/psi(1S)", "p+", "K-"], True))
diff --git a/bandq_resprucing/B2psiX/tools/tupling_maker.py b/bandq_resprucing/B2psiX/tools/tupling_maker.py
new file mode 100644
index 0000000000..fb20000bd3
--- /dev/null
+++ b/bandq_resprucing/B2psiX/tools/tupling_maker.py
@@ -0,0 +1,387 @@
+from DaVinci import make_config, Options
+from DaVinci.algorithms import create_lines_filter
+
+from PyConf.reading import get_rec_summary, get_pvs
+from PyConf.reading import get_particles
+
+import FunTuple.functorcollections as FC
+import Functors as F
+from FunTuple import FunctorCollection
+
+from FunTuple import FunTuple_Event
+from FunTuple import FunTuple_Particles
+from .descriptor_writer import *
+
+
+#from PyConf.reading import (get_particles, get_charged_protoparticles, get_pvs, get_rec_summary, _get_unpacked)
+
+from Functors.math import in_range
+from Hlt2Conf.algorithms_thor import ParticleCombiner, ParticleFilter
+
+from RecoConf.event_filters import require_pvs
+
+from Hlt2Conf.standard_particles import get_long_track_selector, standard_protoparticle_filter
+from PyConf.Algorithms import FunctionalParticleMaker
+from PyConf.reading import get_charged_protoparticles as _make_charged_protoparticles 
+from PyConf.reading import get_odin
+
+from GaudiKernel.SystemOfUnits import MeV, picosecond, mm, GeV
+from DecayTreeFitter import DecayTreeFitter
+
+_basic = "basic"
+_composite = "composite"
+_toplevel = "toplevel"
+
+def all_variables(pvs, mctruth, ptype, candidates=None, ftAlg=None):
+
+    if ptype not in [_basic, _composite]:
+        Exception(f"I want {_basic} or {_composite}. Got {ptype}")
+    all_vars = FunctorCollection({})
+
+    comp = _composite == ptype or _toplevel == ptype  # is composite
+    basic = _basic == ptype  # is not composite
+    top = _toplevel == ptype  # the B
+
+    all_vars += FC.Kinematics()
+    if basic:
+        all_vars += FC.ParticleID(extra_info=True)
+
+    if comp:
+        all_vars.update({"ALV": F.ALV(Child1=1, Child2=2)})
+
+    if comp:  # all these require a vertex
+        all_vars.update({"BPVCORRM": F.BPVCORRM(pvs)})
+        all_vars.update({"BPVCORRMERR": F.BPVCORRMERR(pvs)})
+        all_vars.update({"BPVDIRA": F.BPVDIRA(pvs)})
+        all_vars.update({"BPVDLS": F.BPVDLS(pvs)})
+        all_vars.update({"BPVETA": F.BPVETA(pvs)})
+        all_vars.update({"BPVFD": F.BPVFD(pvs)})
+        all_vars.update({"BPVFDCHI2": F.BPVFDCHI2(pvs)})
+        all_vars.update({"BPVFDIR": F.BPVFDIR(pvs)})
+        all_vars.update({"BPVFDVEC": F.BPVFDVEC(pvs)})
+
+    all_vars.update({"BPVIP": F.BPVIP(pvs)})
+    all_vars.update({"BPVIPCHI2": F.BPVIPCHI2(pvs)})
+    all_vars.update({"BPVX": F.BPVX(pvs)})
+    all_vars.update({"BPVY": F.BPVY(pvs)})
+    all_vars.update({"BPVZ": F.BPVZ(pvs)})
+    all_vars.update({"ALLPVX": F.ALLPVX(pvs)})
+    all_vars.update({"ALLPVY": F.ALLPVY(pvs)})
+    all_vars.update({"ALLPVZ": F.ALLPVZ(pvs)})
+
+    if comp:  # all these require a vertex
+        all_vars.update({"ALLPV_FD": F.ALLPV_FD(pvs)})
+        all_vars.update({"ALLPV_IP": F.ALLPV_IP(pvs)})
+        all_vars.update({"BPVLTIME": F.BPVLTIME(pvs)})
+        all_vars.update({"BPVVDRHO": F.BPVVDRHO(pvs)})
+        all_vars.update({"BPVVDX": F.BPVVDX(pvs)})
+        all_vars.update({"BPVVDY": F.BPVVDY(pvs)})
+        all_vars.update({"BPVVDZ": F.BPVVDZ(pvs)})
+
+    all_vars.update({"CHARGE": F.CHARGE})
+    all_vars.update({"CHI2": F.CHI2})
+    all_vars.update({"CHI2DOF": F.CHI2DOF})
+    #if top:  # apply this only to B
+    #    all_vars.update({"CHILD1_PT": F.CHILD(1, F.PT)})  # example of CHILD
+    #    all_vars.update({"Ds_END_VZ": F.CHILD(1, F.END_VZ)})
+    #    all_vars.update({"Delta_END_VZ_DsB0": F.CHILD(1, F.END_VZ) - F.END_VZ})
+
+    if comp:
+        #all_vars.update({"DOCA": F.SDOCA(Child1=1, Child2=2)})
+        #all_vars.update({"DOCACHI2": F.SDOCACHI2(Child1=1, Child2=2)})
+        all_vars.update({"END_VRHO": F.END_VRHO})
+        all_vars.update({"END_VX": F.END_VX})
+        all_vars.update({"END_VY": F.END_VY})
+        all_vars.update({"END_VZ": F.END_VZ})
+
+    # duplicated from FC   all_vars.update({"ENERGY" : F.ENERGY})
+    all_vars.update({"ETA": F.ETA})
+    all_vars.update({"FOURMOMENTUM": F.FOURMOMENTUM})
+    all_vars.update({"ISBASIC": F.ISBASICPARTICLE})
+
+    if basic:
+        all_vars.update({"GHOSTPROB": F.GHOSTPROB})
+        all_vars.update({"ISMUON": F.ISMUON})
+        all_vars.update({"INMUON": F.INMUON})
+        all_vars.update({"INECAL": F.INECAL})
+        all_vars.update({"INHCAL": F.INHCAL})
+        all_vars.update({"HASBREM": F.HASBREM})
+        all_vars.update({"BREMENERGY": F.BREMENERGY})
+        all_vars.update({"BREMBENDCORR": F.BREMBENDCORR})
+        all_vars.update({"BREMPIDE": F.BREMPIDE})
+        all_vars.update({"ECALPIDE": F.ECALPIDE})
+        all_vars.update({"ECALPIDMU": F.ECALPIDMU})
+        all_vars.update({"HCALPIDE": F.HCALPIDE})
+        all_vars.update({"HCALPIDMU": F.HCALPIDMU})
+        all_vars.update({"ELECTRONSHOWEREOP": F.ELECTRONSHOWEREOP})
+        all_vars.update({"CLUSTERMATCH": F.CLUSTERMATCH_CHI2})
+        all_vars.update({"ELECTRONMATCH": F.ELECTRONMATCH_CHI2})
+        all_vars.update({"BREMHYPOMATCH": F.BREMHYPOMATCH_CHI2})
+        all_vars.update({"ELECTRONENERGY": F.ELECTRONENERGY})
+        all_vars.update({"BREMHYPOENERGY": F.BREMHYPOENERGY})
+        all_vars.update({"BREMHYPODELTAX": F.BREMHYPODELTAX})
+        all_vars.update({"ELECTRONID": F.ELECTRONID})
+        all_vars.update({"HCALEOP": F.HCALEOP})
+        all_vars.update({"TRACK_MOM_": F.TRACK_MOMVEC})
+        #all_vars.update({"TRACK_POS_CLOSESTTOBEAM_": F.TRACK_POSVEC_CLOSESTTOBEAM})
+        all_vars.update({"IS_ID_pi": F.IS_ID("pi-")})
+        all_vars.update({"PDG_MASS_pi": F.PDG_MASS("pi+")})
+        all_vars.update({"SIGNED_DELTA_MASS_pi": F.SIGNED_DELTA_MASS("pi+")})
+        all_vars.update({"ABS_DELTA_MASS_pi": F.ABS_DELTA_MASS("pi+")})
+        all_vars.update({"IS_NOT_H": F.IS_NOT_H})
+        all_vars.update({"IS_PHOTON": F.IS_PHOTON})
+
+    if comp:
+        all_vars.update({"MAXPT": F.MAX(F.PT)})
+        all_vars.update({"MAXDOCA": F.MAXSDOCA})
+        all_vars.update({"MAXDOCACHI2": F.MAXSDOCACHI2})
+        #all_vars.update({"MINDOCA": F.MINSDOCA})
+        #all_vars.update({"MINDOCACHI2": F.MINSDOCACHI2})
+        # the above in cut versions.
+
+    # duplicated from FC    all_vars.update({ 'MC_MOTHER_ID' : F.VALUE_OR(0) @ mctruth(
+    # duplicated from FC        F.MC_MOTHER(1, F.PARTICLE_ID))})
+
+    if comp:
+        all_vars.update({"MINPT": F.MIN(F.PT)})
+    all_vars.update({"MINIP": F.MINIP(pvs)})
+    all_vars.update({"MINIPCHI2": F.MINIPCHI2(pvs)})
+
+    if basic:
+        all_vars.update({"TRACKPT": F.TRACK_PT})
+        all_vars.update({"TRACKTYPE": F.VALUE_OR(-1) @ F.TRACKTYPE @ F.TRACK})
+        all_vars.update({"TRACKHISTORY": F.VALUE_OR(-1) @ F.TRACKHISTORY @ F.TRACK})
+        all_vars.update({"QOVERP": F.QOVERP @ F.TRACK})
+        all_vars.update({"NDOF": F.VALUE_OR(-1) @ F.NDOF @ F.TRACK})
+        all_vars.update({"NFTHITS": F.VALUE_OR(-1) @ F.NFTHITS @ F.TRACK})
+        all_vars.update({"NHITS": F.VALUE_OR(-1) @ F.NHITS @ F.TRACK})
+        all_vars.update({"NUTHITS": F.VALUE_OR(-1) @ F.NUTHITS @ F.TRACK})
+        all_vars.update({"NVPHITS": F.VALUE_OR(-1) @ F.NVPHITS @ F.TRACK})
+        all_vars.update({"TRACKHASVELO": F.VALUE_OR(-1) @ F.TRACKHASVELO @ F.TRACK})
+        all_vars.update({"TRACKHASUT": F.VALUE_OR(-1) @ F.TRACKHASUT @ F.TRACK})
+
+    all_vars.update({"OBJECT_KEY": F.OBJECT_KEY})
+    all_vars.update({"PHI": F.PHI})
+
+    all_vars.update({"ABS_PX": F.ABS @ F.PX})
+
+    all_vars.update({"REFERENCEPOINT_X": F.REFERENCEPOINT_X})
+    all_vars.update({"REFERENCEPOINT_Y": F.REFERENCEPOINT_Y})
+    all_vars.update({"REFERENCEPOINT_Z": F.REFERENCEPOINT_Z})
+
+    if comp:
+        all_vars.update({"SDOCA12": F.SDOCA(1, 2)})
+        all_vars.update({"SDOCA12_CHI2": F.SDOCACHI2(1, 2)})
+    if basic:
+        all_vars.update({"SHOWER_SHAPE": F.CALO_NEUTRAL_SHOWER_SHAPE})
+
+    if comp:
+        all_vars.update({"SUBCOMB12_MM": F.SUBCOMB(Functor=F.MASS, Indices=(1, 2))})
+        all_vars.update({"SUMPT": F.SUM(F.PT)})
+    if top:
+        all_vars.update({"SDOCA13": F.SDOCA(1, 3)})
+        all_vars.update({"SDOCA13_CHI2": F.SDOCACHI2(1, 3)})
+        all_vars.update({"SDOCA23": F.SDOCA(2, 3)})
+        all_vars.update({"SDOCA23_CHI2": F.SDOCACHI2(2, 3)})
+        all_vars.update({"SUBCOMB13_MM": F.SUBCOMB(Functor=F.MASS, Indices=(1, 3))})
+        all_vars.update({"SUBCOMB23_MM": F.SUBCOMB(Functor=F.MASS, Indices=(2, 3))})
+
+    if basic:
+        all_vars.update({"TX": F.TX})
+        all_vars.update({"TY": F.TY})
+
+    print(f"### For {ptype} returning variables {all_vars.functor_dict.keys()}")
+    return all_vars                                                                                                                      
+
+def tistos_variables(Hlt1_decisions, Hlt2_decisions, data, isturbo):
+    tistos_vars = FunctorCollection({})
+    tistos_vars += FC.HltTisTos( selection_type="Hlt1", trigger_lines=Hlt1_decisions, data=data)
+    if not isturbo:
+        tistos_vars += FC.HltTisTos( selection_type="Hlt2", trigger_lines=Hlt2_decisions, data=data)
+    return tistos_vars
+
+def event_variables(PVs, ODIN, decreports, rec_sum, hlt1_lines, sprucing_lines):
+    """
+    event variables
+    """
+
+    ## Some empty summaries removed
+    evt_vars = FunctorCollection({
+        "nPVs": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nPVs"),
+        "nTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nTracks"),
+        "nLongTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nLongTracks"),
+        "nDownstreamTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nDownstreamTracks"),
+        "nUpstreamTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nUpstreamTracks"),
+        "nVeloTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVeloTracks"),
+        "nBackTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nBackTracks"),
+        "nGhosts": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nGhosts"),
+        "nRich1Hits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nRich1Hits"),
+        "nRich2Hits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nRich2Hits"),
+        #"nVeloClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVeloClusters"),
+        "nVPClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVPClusters"),
+        #"nITClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nITClusters"),
+        #"nTTClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nTTClusters"),
+        "nUTClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nUTClusters"),
+        #"nOTClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nOTClusters"),
+        "nFTClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nFTClusters"),
+        #"nSPDhits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nSPDhits"),
+        "eCalTot": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"eCalTot"),
+        "hCalTot": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"hCalTot"),
+        "nEcalClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nEcalClusters"),
+        #"nMuonCoordsS0": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nMuonCoordsS0"),
+        #"nMuonCoordsS1": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nMuonCoordsS1"),
+        #"nMuonCoordsS2": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nMuonCoordsS2"),
+        #"nMuonCoordsS3": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nMuonCoordsS3"),
+        #"nMuonCoordsS4": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nMuonCoordsS4"),
+        #"nMuonTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nMuonTracks"),
+        "ALLPVX": F.ALLPVX(PVs),
+        "ALLPVY": F.ALLPVY(PVs),
+        "ALLPVZ": F.ALLPVZ(PVs),
+        })
+    evt_vars += FC.EventInfo()
+
+    evt_vars += FC.SelectionInfo( selection_type="Hlt1", trigger_lines=hlt1_lines)                                               
+    evt_vars += FC.SelectionInfo( selection_type="Hlt2", trigger_lines=sprucing_lines)
+    # duplicated from FC    if ODIN:
+    # duplicated from FC        evt_vars.update({ 'BUNCHCROSSING_ID' : F.BUNCHCROSSING_ID(ODIN)})
+    # duplicated from FC        evt_vars.update({ 'BUNCHCROSSING_TYPE' : F.BUNCHCROSSING_TYPE(ODIN)})
+
+    if decreports:
+        evt_vars.update(
+                {
+                    "DECISIONS": F.DECISIONS(
+                        Lines=[bd2dsk_line + "Decision"], DecReports=decreports
+                        )
+                    }
+                )
+        evt_vars.update(
+                {
+                    "DECREPORTS_FILTER": F.DECREPORTS_FILTER(
+                        Lines=[bd2dsk_line + "Decision"], DecReports=decreports
+                        )
+                    }
+                )
+
+    if ODIN:
+        evt_vars.update({"EVENTTYPE": F.EVENTTYPE(ODIN)})
+
+    # duplicated from FC        evt_vars.update({ 'GPSTIME' : F.GPSTIME(ODIN)})
+    # duplicated from FC        evt_vars.update({ 'ODINTCK' : F.ODINTCK(ODIN)})
+
+    evt_vars.update({"PV_SIZE": F.SIZE(PVs)})
+    # duplicated from FC        evt_vars.update({ 'GPSTIME' : F.GPSTIME(ODIN)})
+    # duplicated from FC        evt_vars.update({ 'ODINTCK' : F.ODINTCK(ODIN)})
+
+    if decreports:
+        evt_vars.update({"TCK": F.TCK(decreports)})
+
+    print(f"### For event returning variables {evt_vars.functor_dict.keys()}")
+    return evt_vars
+
+def candidate_variables(pvs, particles):
+    abbrs = default_names(particles)
+    names = all_particles(particles)
+    variables_B = {abbr: all_variables(pvs, None, particle_df["type"][name]) for abbr, name in zip(abbrs, names)}
+    return variables_B
+
+from DecayTreeFitter import DecayTreeFitter
+def make_dtf_variables(pvs, data, particles, pv_constraint=False, mass_constraints=[], label_forLLDD=""):
+
+    abbrs = default_names(particles)
+    names = all_particles(particles)
+
+    if pv_constraint: dtf_name = "DTF_PV_"
+    else: dtf_name = "DTF_"
+    dtf_name += "".join(f"{par}_" for par in particle_df["abbr"][mass_constraints])
+    my_hash='{hash}'
+
+    DTF = DecayTreeFitter(
+            name = f"{dtf_name}DecayTreeFitter_{my_hash}",
+            input_particles = data,
+            input_pvs = (pv_constraint and pvs),
+            mass_constraints = mass_constraints,
+            )
+
+    dtf_dict = {}
+
+    shared_variables = FunctorCollection(
+                {   "ETA": F.ETA,
+                    "PHI": F.PHI,
+                    "BPVIPCHI2": F.BPVIPCHI2(pvs),
+                    "BPVIP": F.BPVIP(pvs),
+                    "BPVX": F.BPVX(pvs),
+                    "BPVY": F.BPVY(pvs),
+                    "BPVZ": F.BPVZ(pvs),
+                    }
+                ) + FC.Kinematics()
+
+    dtf_quality_variables = FunctorCollection( {
+            dtf_name+"_DTFCHI2": DTF.CHI2,
+            dtf_name+"_DTFNDOF": DTF.NDOF,
+            dtf_name+"_CTAU": DTF.CTAU,
+            dtf_name+"_CTAUERR": DTF.CTAUERR,
+            dtf_name+"_MERR": DTF.MASSERR,
+            }
+        )
+
+    #make branches
+    for abbr, name in zip(abbrs, names):
+        is_basic = particle_df["type"][name]
+        if is_basic:
+            orig_variables = shared_variables + FunctorCollection(
+                    {   "TX"          : F.TX,
+                        "TY"          : F.TY,
+                        "MINIPCHI2"   : F.MINIPCHI2(pvs),
+                        "MINIP"       : F.MINIP(pvs),
+                        "KEY"         : F.VALUE_OR(-1) @ F.OBJECT_KEY @ F.TRACK,
+                        "TRGHOSTPROB": F.GHOSTPROB,
+                        "TRACKPT": F.TRACK_PT,
+                        "TRACKHISTORY": F.VALUE_OR(-1) @ F.TRACKHISTORY @ F.TRACK,
+                        "QOVERP": F.QOVERP @ F.TRACK,
+                        "TRCHI2DOF": F.CHI2DOF @ F.TRACK,
+                        "NDOF": F.VALUE_OR(-1) @ F.NDOF @ F.TRACK,
+                        }
+                    )
+        else:
+            orig_variables = shared_variables + FunctorCollection(
+                {   "MAXPT": F.MAX(F.PT),
+                    "MINPT": F.MIN(F.PT),
+                    "SUMPT": F.SUM(F.PT),
+                    "MAXP": F.MAX(F.P),
+                    "MINP": F.MIN(F.P),
+                    "BPVDIRA": F.BPVDIRA(pvs),
+                    "CHI2DOF": F.CHI2DOF, #CHI2VXNDOF
+                    "BPVFDCHI2": F.BPVFDCHI2(pvs),
+                    "BPVFD": F.BPVFD(pvs),
+                    "BPVVDRHO": F.BPVVDRHO(pvs),
+                    "BPVVDZ": F.BPVVDZ(pvs),
+                    "BPVLTIME": F.BPVLTIME(pvs),
+                    "END_VX": F.END_VX, #END_
+                    "END_VY": F.END_VY,
+                    "END_VZ": F.END_VZ,
+                    }
+                )
+            orig_variables += dtf_quality_variables
+
+        dtf_variables = FunctorCollection({ dtf_name + expr: DTF(func) for expr, func in orig_variables.get_thor_functors().items() })
+        dtf_dict.update( {abbr: dtf_variables} )
+
+    return dtf_dict
+
+import re
+def convert_cut(string_cut):
+    cuts = string_cut.split("&")
+    paras = ["BPV", "PID_", "MASS", "CHARGE", "CHI2DOF", "END", "IS_ID", "ABS_", "MAX", "SUM", "GHOSTPROB", "CHILD"]
+    values = []
+    for cut in cuts:
+        value = cut
+        for para in paras:
+            if para in cut:
+                value = value.replace(para, f"F.{para}")
+        value = re.sub(r"\bPT\b", "F.PT", value)
+        value = re.sub(r"\bP\b", "F.P", value)
+        value = re.sub(r"\bCHI2\b", "F.CHI2", value)
+        values.append(value)
+    functor_cut = ",".join(values)
+    return eval(f"F.require_all({functor_cut})")
+#return f"F.require_all({functor_cut})"
diff --git a/bandq_resprucing/BforSpectroscopy/dv_data.py b/bandq_resprucing/BforSpectroscopy/dv_data.py
new file mode 100644
index 0000000000..6dd5300fc3
--- /dev/null
+++ b/bandq_resprucing/BforSpectroscopy/dv_data.py
@@ -0,0 +1,949 @@
+from .tupling_maker import template, line_prefilter
+from DaVinci import Options, make_config
+
+# Bu 7
+# Bd 6
+# Bs 6
+# Bc 3
+# Lb 4
+# Xibm 5-1
+# Xib0 5-1
+# Omegab 6-1
+
+HLT2_lines = ["Hlt2Topo2BodyDecision", "Hlt2Topo3BodyDecision", "Hlt2_JpsiToMuMuDetachedFull"]
+
+
+def Bu():
+    
+    # BuToJpsiK = make_BuToJpsiK(process)
+    # BuToD0Pi = make_BuToD0Pi(process)
+    # BuToD0PiPiPi = make_BuToD0PiPiPi(process)
+    # BuToDmPiPi = make_BuToDmPiPi(process)
+
+    decays = [
+        # Bu -> J/psi K+ decay
+        (
+            {
+                "Bu":   "[B+ -> (J/psi(1S) -> mu+ mu-) K+]CC",
+                "Jpsi": "[B+ -> ^(J/psi(1S) -> mu+ mu-) K+]CC",
+                "Jpsi_mup":  "[B+ -> (J/psi(1S) -> ^mu+ mu-) K+]CC",
+                "Jpsi_mum":  "[B+ -> (J/psi(1S) -> mu+ ^mu-) K+]CC",
+                "Bu_K1":    "[B+ -> (J/psi(1S) -> mu+ mu-) ^K+]CC"
+            },
+            ["Bu", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Bu_K1"],
+            "BuToJpsiK",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+        
+        # Bu -> D0 pi+ decay, with D0 -> K- pi+
+        (
+            {
+                "Bu":   "[B+ -> (D~0 -> K+ pi-) pi+]CC",
+                "D0":   "[B+ -> ^(D~0 -> K+ pi-) pi+]CC",
+                "D0_K1":    "[B+ -> (D~0 -> ^K+ pi-) pi+]CC",
+                "D0_Pi1": "[B+ -> (D~0 -> K+ ^pi-) pi+]CC",
+                "Bu_Pi1": "[B+ -> (D~0 -> K+ pi-) ^pi+]CC"
+            },
+            ["Bu", "D0"],
+            ["D0_K1", "D0_Pi1", "Bu_Pi1"],
+            "BuToD0Pi",
+            ["D~0", "D0"],
+        ),
+
+        # Bu -> D0 pi+ pi+ pi- decay, with D0 -> K- pi+
+        (
+            {
+                "Bu":   "[B+ -> (D~0 -> K+ pi-) pi+ pi+ pi-]CC",
+                "D0":   "[B+ -> ^(D~0 -> K+ pi-) pi+ pi+ pi-]CC",
+                "D0_K1":    "[B+ -> (D~0 -> ^K+ pi-) pi+ pi+ pi-]CC",
+                "D0_Pi1": "[B+ -> (D~0 -> K+ ^pi-) pi+ pi+ pi-]CC",
+                "Bu_Pi1": "[B+ -> (D~0 -> K+ pi-) ^pi+ pi+ pi-]CC",
+                "Bu_Pi2": "[B+ -> (D~0 -> K+ pi-) pi+ ^pi+ pi-]CC",
+                "Bu_Pi3": "[B+ -> (D~0 -> K+ pi-) pi+ pi+ ^pi-]CC"
+            },
+            ["Bu", "D0"],
+            ["D0_K1", "D0_Pi1", "Bu_Pi1", "Bu_Pi2", "Bu_Pi3"],
+            "BuToD0PiPiPi",
+            ["D~0", "D0"],
+        ),
+
+        # Bu -> D- pi+ pi+ decay, with D+ -> K- pi+ pi+
+        (
+            {
+                "Bu":   "[B+ -> (D- -> K+ pi- pi-) pi+ pi+]CC",
+                "Dm":   "[B+ -> ^(D- -> K+ pi- pi-) pi+ pi+]CC",
+                "Dm_K1":    "[B+ -> (D- -> ^K+ pi- pi-) pi+ pi+]CC",
+                "Dm_Pi1": "[B+ -> (D- -> K+ ^pi- pi-) pi+ pi+]CC",
+                "Dm_Pi2": "[B+ -> (D- -> K+ pi- ^pi-) pi+ pi+]CC",
+                "Bu_Pi1": "[B+ -> (D- -> K+ pi- pi-) ^pi+ pi+]CC",
+                "Bu_Pi2": "[B+ -> (D- -> K+ pi- pi-) pi+ ^pi+]CC"
+            },
+            ["Bu", "Dm"],
+            ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bu_Pi1", "Bu_Pi2"],
+            "BuToDmPipPip",
+            ["D-", "Dm"],
+        ),
+
+
+        # Bu -> D0 pi+ decay, with D0 -> K- pi+ pi- pi+
+        (
+            {
+                "Bu":   "[B+ -> (D~0 -> K+ pi+ pi- pi-) pi+]CC",
+                "D0":   "[B+ -> ^(D~0 -> K+ pi+ pi- pi-) pi+]CC",
+                "D0_K1":    "[B+ -> (D~0 -> ^K+ pi+ pi- pi-) pi+]CC",
+                "D0_Pi1": "[B+ -> (D~0 -> K+ ^pi+ pi- pi-) pi+]CC",
+                "D0_Pi2": "[B+ -> (D~0 -> K+ pi+ ^pi- pi-) pi+]CC",
+                "D0_Pi3": "[B+ -> (D~0 -> K+ pi+ pi- ^pi-) pi+]CC",
+                "Bu_Pi1": "[B+ -> (D~0 -> K+ pi+ pi- pi-) ^pi+]CC"
+            },
+            ["Bu", "D0"],
+            ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bu_Pi1"],
+            "Bu2D0Pi_K3Pi",
+            ["D~0", "D0"],
+        ),
+
+        # Bu -> D0 pi+ pi+ pi- decay, with D0 -> K- pi+ pi- pi+
+        (
+            {
+                "Bu":   "[B+ -> (D~0 -> K+ pi+ pi- pi-) pi+ pi+ pi-]CC",
+                "D0":   "[B+ -> ^(D~0 -> K+ pi+ pi- pi-) pi+ pi+ pi-]CC",
+                "D0_K1":    "[B+ -> (D~0 -> ^K+ pi+ pi- pi-) pi+ pi+ pi-]CC",
+                "D0_Pi1": "[B+ -> (D~0 -> K+ ^pi+ pi- pi-) pi+ pi+ pi-]CC",
+                "D0_Pi2": "[B+ -> (D~0 -> K+ pi+ ^pi- pi-) pi+ pi+ pi-]CC",
+                "D0_Pi3": "[B+ -> (D~0 -> K+ pi+ pi- ^pi-) pi+ pi+ pi-]CC",
+                "Bu_Pi1": "[B+ -> (D~0 -> K+ pi+ pi- pi-) ^pi+ pi+ pi-]CC",
+                "Bu_Pi2": "[B+ -> (D~0 -> K+ pi+ pi- pi-) pi+ ^pi+ pi-]CC",
+                "Bu_Pi3": "[B+ -> (D~0 -> K+ pi+ pi- pi-) pi+ pi+ ^pi-]CC"
+            },
+            ["Bu", "D0"],
+            ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bu_Pi1", "Bu_Pi2", "Bu_Pi3"],
+            "Bu2D0PiPiPi_K3Pi",
+            ["D~0", "D0"],
+        )
+    ]
+    
+    line_name = 'SpruceBandQ_BuForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def Bd():
+    
+    # BdToJpsiKPi = make_BdToJpsiKPi(process)
+    # BdToDmPip = make_BdToDmPip(process)
+    # BdToD0barPiPi = make_BdToD0barPiPi(process)
+    # BdToDmPipPipPim = make_BdToDmPipPipPim(process)
+    
+    decays = [
+        # Bd -> J/psi K+ pi- decay
+        (
+            {
+                "Bd":   "[B0 -> (J/psi(1S) -> mu+ mu-) K+ pi-]CC",
+                "Jpsi": "[B0 -> ^(J/psi(1S) -> mu+ mu-) K+ pi-]CC",
+                "Jpsi_mup":  "[B0 -> (J/psi(1S) -> ^mu+ mu-) K+ pi-]CC",
+                "Jpsi_mum":  "[B0 -> (J/psi(1S) -> mu+ ^mu-) K+ pi-]CC",
+                "Bd_K1":    "[B0 -> (J/psi(1S) -> mu+ mu-) ^K+ pi-]CC",
+                "Bd_Pi1":   "[B0 -> (J/psi(1S) -> mu+ mu-) K+ ^pi-]CC"
+            },
+            ["Bd", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Bd_K1", "Bd_Pi1"],
+            "BdToJpsiKPi",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+
+        # Bd -> D+ pi- decay, with D+ -> K- pi+ pi+
+        (
+            {
+                "Bd":   "[B0 -> (D- -> K+ pi- pi-) pi+]CC",
+                "Dm":   "[B0 -> ^(D- -> K+ pi- pi-) pi+]CC",
+                "Dm_K1":    "[B0 -> (D- -> ^K+ pi- pi-) pi+]CC",
+                "Dm_Pi1": "[B0 -> (D- -> K+ ^pi- pi-) pi+]CC",
+                "Dm_Pi2": "[B0 -> (D- -> K+ pi- ^pi-) pi+]CC",
+                "Bd_Pi1":  "[B0 -> (D- -> K+ pi- pi-) ^pi+]CC"
+            },
+            ["Bd", "Dm"],
+            ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bd_Pi1"],
+            "BdToDmPip",
+            ["D-", "Dm"],
+        ),
+
+        # Bd -> D0bar pi+ pi- decay, with D0bar -> K+ pi-
+        (
+            {
+                "Bd":    "[B0 -> (D~0 -> K+ pi-) pi+ pi-]CC",
+                "D0":   "[B0 -> ^(D~0 -> K+ pi-) pi+ pi-]CC",
+                "D0_K1":     "[B0 -> (D~0 -> ^K+ pi-) pi+ pi-]CC",
+                "D0_Pi1":  "[B0 -> (D~0 -> K+ ^pi-) pi+ pi-]CC",
+                "Bd_Pi1": "[B0 -> (D~0 -> K+ pi-) ^pi+ pi-]CC",
+                "Bd_Pi2": "[B0 -> (D~0 -> K+ pi-) pi+ ^pi-]CC"
+            },
+            ["Bd", "D0"],
+            ["D0_K1", "D0_Pi1", "Bd_Pi1", "Bd_Pi2"],
+            "BdToD0barPiPi",
+            ["D~0", "D0"],
+        ),
+
+        # Bd -> D- pi+ pi+ pi- decay, with D+ -> K- pi+ pi+
+        (
+            {
+                "Bd":    "[B0 -> (D- -> K+ pi- pi-) pi+ pi+ pi-]CC",
+                "Dm":    "[B0 -> ^(D- -> K+ pi- pi-) pi+ pi+ pi-]CC",
+                "Dm_K1":     "[B0 -> (D- -> ^K+ pi- pi-) pi+ pi+ pi-]CC",
+                "Dm_Pi1": "[B0 -> (D- -> K+ ^pi- pi-) pi+ pi+ pi-]CC",
+                "Dm_Pi2": "[B0 -> (D- -> K+ pi- ^pi-) pi+ pi+ pi-]CC",
+                "Bd_Pi1": "[B0 -> (D- -> K+ pi- pi-) ^pi+ pi+ pi-]CC",
+                "Bd_Pi2": "[B0 -> (D- -> K+ pi- pi-) pi+ ^pi+ pi-]CC",
+                "Bd_Pi3": "[B0 -> (D- -> K+ pi- pi-) pi+ pi+ ^pi-]CC"
+            },
+            ["Bd", "Dm"],
+            ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bd_Pi1", "Bd_Pi2", "Bd_Pi3"],
+            "BdToDmPipPipPim",
+            ["D-", "Dm"],
+        ),
+        # Bd -> D0bar pi+ pi-, with D0bar -> K+ pi- pi+ pi-
+        (
+            {
+                "Bd":    "[B0 -> (D~0 -> K+ pi- pi+ pi-) pi+ pi-]CC",
+                "D0":   "[B0 -> ^(D~0 -> K+ pi- pi+ pi-) pi+ pi-]CC",
+                "D0_K1":     "[B0 -> (D~0 -> ^K+ pi- pi+ pi-) pi+ pi-]CC",
+                "D0_Pi1": "[B0 -> (D~0 -> K+ ^pi- pi+ pi-) pi+ pi-]CC",
+                "D0_Pi2": "[B0 -> (D~0 -> K+ pi- ^pi+ pi-) pi+ pi-]CC",
+                "D0_Pi3": "[B0 -> (D~0 -> K+ pi- pi+ ^pi-) pi+ pi-]CC",
+                "Bd_Pi1": "[B0 -> (D~0 -> K+ pi- pi+ pi-) ^pi+ pi-]CC",
+                "Bd_Pi2": "[B0 -> (D~0 -> K+ pi- pi+ pi-) pi+ ^pi-]CC"
+            },
+            ["Bd", "D0"],
+            ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bd_Pi1", "Bd_Pi2"],
+            "BdToD0barPiPi_K3Pi",
+            ["D~0", "D0"],
+        )
+    ]
+    
+    
+    line_name = 'SpruceBandQ_BdForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def Bs():
+    
+    # BsToJpsiKK = make_BsToJpsiKK(process)
+    # BsToDsmPip = make_BsToDsmPip(process)
+    # BsToDsmPipPipPim = make_BsToDsmPipPipPim(process)
+    # BsToD0bKmPip = make_BsToD0bKmPip(process)
+    
+    decays = [
+        # Bs -> J/psi K+ K- decay
+        (
+            {
+                "Bs":   "[B_s0 -> (J/psi(1S) -> mu+ mu-) K+ K-]CC",
+                "Jpsi": "[B_s0 -> ^(J/psi(1S) -> mu+ mu-) K+ K-]CC",
+                "Jpsi_mup":  "[B_s0 -> (J/psi(1S) -> ^mu+ mu-) K+ K-]CC",
+                "Jpsi_mum":  "[B_s0 -> (J/psi(1S) -> mu+ ^mu-) K+ K-]CC",
+                "Bs_K1":   "[B_s0 -> (J/psi(1S) -> mu+ mu-) ^K+ K-]CC",
+                "Bs_K2":   "[B_s0 -> (J/psi(1S) -> mu+ mu-) K+ ^K-]CC"
+            },
+            ["Bs", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Bs_K1", "Bs_K2"],
+            "BsToJpsiKK",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+        
+        # Bs -> D_s- pi+ decay (corrected)
+        (
+            {
+                "Bs":   "[B_s0 -> (D_s- -> K- K+ pi-) pi+]CC",
+                "Ds": "[B_s0 -> ^(D_s- -> K- K+ pi-) pi+]CC",
+                "Ds_K1":   "[B_s0 -> (D_s- -> ^K- K+ pi-) pi+]CC",
+                "Ds_K2":   "[B_s0 -> (D_s- -> K- ^K+ pi-) pi+]CC",
+                "Ds_Pi1": "[B_s0 -> (D_s- -> K- K+ ^pi-) pi+]CC",
+                "Bs_Pi1": "[B_s0 -> (D_s- -> K- K+ pi-) ^pi+]CC"
+            },
+            ["Bs", "Ds"],
+            ["Ds_K1", "Ds_K2", "Ds_Pi1", "Bs_Pi1"],
+            "BsToDsmPip",
+            ["D_s-", "Ds"],
+        ),
+
+        # Bs -> D_s- pi+ pi+ pi- decay (corrected)
+        (
+            {
+                "Bs":   "[B_s0 -> (D_s- -> K- K+ pi-) pi+ pi+ pi-]CC",
+                "Ds": "[B_s0 -> ^(D_s- -> K- K+ pi-) pi+ pi+ pi-]CC",
+                "Ds_K1":   "[B_s0 -> (D_s- -> ^K- K+ pi-) pi+ pi+ pi-]CC",
+                "Ds_K2":   "[B_s0 -> (D_s- -> K- ^K+ pi-) pi+ pi+ pi-]CC",
+                "Ds_Pi1": "[B_s0 -> (D_s- -> K- K+ ^pi-) pi+ pi+ pi-]CC",
+                "Bs_Pi1": "[B_s0 -> (D_s- -> K- K+ pi-) ^pi+ pi+ pi-]CC",
+                "Bs_Pi2": "[B_s0 -> (D_s- -> K- K+ pi-) pi+ ^pi+ pi-]CC",
+                "Bs_Pi3": "[B_s0 -> (D_s- -> K- K+ pi-) pi+ pi+ ^pi-]CC"
+            },
+            ["Bs", "Ds"],
+            ["Ds_K1", "Ds_K2", "Ds_Pi1", "Bs_Pi1", "Bs_Pi2", "Bs_Pi3"],
+            "BsToDsmPipPipPim",
+            ["D_s-", "Ds"],
+        ),
+        
+        # Bs -> D0bar K- pi+, with D0bar -> K+ pi- pi+ pi-
+        (
+            {
+                "Bs":   "[B_s0 -> (D~0 -> K+ pi- pi+ pi-) K- pi+]CC",
+                "D0":  "[B_s0 -> ^(D~0 -> K+ pi- pi+ pi-) K- pi+]CC",
+                "D0_K1":   "[B_s0 -> (D~0 -> ^K+ pi- pi+ pi-) K- pi+]CC",
+                "D0_Pi1": "[B_s0 -> (D~0 -> K+ ^pi- pi+ pi-) K- pi+]CC",
+                "D0_Pi2": "[B_s0 -> (D~0 -> K+ pi- ^pi+ pi-) K- pi+]CC",
+                "D0_Pi3": "[B_s0 -> (D~0 -> K+ pi- pi+ ^pi-) K- pi+]CC",
+                "Bs_K1":   "[B_s0 -> (D~0 -> K+ pi- pi+ pi-) ^K- pi+]CC",
+                "Bs_Pi1":  "[B_s0 -> (D~0 -> K+ pi- pi+ pi-) K- ^pi+]CC"
+            },
+            ["Bs", "D0"],
+            ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bs_K1", "Bs_Pi1"],
+            "BsToD0bKmPip_K3Pi",
+            ["D~0", "D0"],
+        ),
+
+        # Bs -> D~0 K- pi+ decay
+        (
+            {
+                "Bs":   "[B_s0 -> (D~0 -> K+ pi-) K- pi+]CC",
+                "D0":  "[B_s0 -> ^(D~0 -> K+ pi-) K- pi+]CC",
+                "D0_K1":   "[B_s0 -> (D~0 -> ^K+ pi-) K- pi+]CC",
+                "D0_Pi1": "[B_s0 -> (D~0 -> K+ ^pi-) K- pi+]CC",
+                "Bs_K1":   "[B_s0 -> (D~0 -> K+ pi-) ^K- pi+]CC",
+                "Bs_Pi1":  "[B_s0 -> (D~0 -> K+ pi-) K- ^pi+]CC"
+            },
+            ["Bs", "D0"],
+            ["D0_K1", "D0_Pi1", "Bs_K1", "Bs_Pi1"],
+            "BsToD0bKmPip",
+            ["D~0", "D0"],
+        )
+    ]
+    
+    
+    line_name = 'SpruceBandQ_BsForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def Bc():
+    
+    # BcToJpsiPi = make_BcToJpsiPi(process)
+    # BcToJpsiPiPiPi = make_BcToJpsiPiPiPi(process)
+    
+    decays = [
+        # Bc -> J/psi pi+ decay
+        (
+            {
+                "Bc":   "[B_c+ -> (J/psi(1S) -> mu+ mu-) pi+]CC",
+                "Jpsi": "[B_c+ -> ^(J/psi(1S) -> mu+ mu-) pi+]CC",
+                "Jpsi_mup":  "[B_c+ -> (J/psi(1S) -> ^mu+ mu-) pi+]CC",
+                "Jpsi_mum":  "[B_c+ -> (J/psi(1S) -> mu+ ^mu-) pi+]CC",
+                "Bc_Pi1":   "[B_c+ -> (J/psi(1S) -> mu+ mu-) ^pi+]CC"
+            },
+            ["Bc", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Bc_Pi1"],
+            "BcToJpsiPi",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+
+        # Bc -> J/psi pi+ pi+ pi- decay
+        (
+            {
+                "Bc":   "[B_c+ -> (J/psi(1S) -> mu+ mu-) pi+ pi+ pi-]CC",
+                "Jpsi": "[B_c+ -> ^(J/psi(1S) -> mu+ mu-) pi+ pi+ pi-]CC",
+                "Jpsi_mup":  "[B_c+ -> (J/psi(1S) -> ^mu+ mu-) pi+ pi+ pi-]CC",
+                "Jpsi_mum":  "[B_c+ -> (J/psi(1S) -> mu+ ^mu-) pi+ pi+ pi-]CC",
+                "Bc_Pi1":  "[B_c+ -> (J/psi(1S) -> mu+ mu-) ^pi+ pi+ pi-]CC",
+                "Bc_Pi2":  "[B_c+ -> (J/psi(1S) -> mu+ mu-) pi+ ^pi+ pi-]CC",
+                "Bc_Pi3":  "[B_c+ -> (J/psi(1S) -> mu+ mu-) pi+ pi+ ^pi-]CC"
+            },
+            ["Bc", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Bc_Pi1", "Bc_Pi2", "Bc_Pi3"],
+            "BcToJpsiPiPiPi",
+            ["J/psi(1S)", "Jpsi"],
+        )
+    ]
+    
+    
+    line_name = 'SpruceBandQ_BcForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def Lb():
+    
+    # LbToJpsiPK = make_LbToJpsiPK(process)
+    # LbToLcPim = make_LbToLcPim(process)
+    # LbToLcPipPimPim = make_LbToLcPipPimPim(process)
+    decays = [
+        # Lb -> J/psi p+ K- decay
+        (
+            {
+                "Lb":   "[Lambda_b0 -> (J/psi(1S) -> mu+ mu-) p+ K-]CC",
+                "Jpsi": "[Lambda_b0 -> ^(J/psi(1S) -> mu+ mu-) p+ K-]CC",
+                "Jpsi_mup":  "[Lambda_b0 -> (J/psi(1S) -> ^mu+ mu-) p+ K-]CC",
+                "Jpsi_mum":  "[Lambda_b0 -> (J/psi(1S) -> mu+ ^mu-) p+ K-]CC",
+                "Lb_P1":    "[Lambda_b0 -> (J/psi(1S) -> mu+ mu-) ^p+ K-]CC",
+                "Lb_K1":    "[Lambda_b0 -> (J/psi(1S) -> mu+ mu-) p+ ^K-]CC"
+            },
+            ["Lb", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Lb_P1", "Lb_K1"],
+            "LbToJpsiPK",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+
+        # Lb -> Lc+ pi- decay, with Lc+ -> p+ K- pi+
+        (
+            {
+                "Lb":   "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) pi-]CC",
+                "Lc":   "[Lambda_b0 -> ^(Lambda_c+ -> p+ K- pi+) pi-]CC",
+                "Lc_P1":    "[Lambda_b0 -> (Lambda_c+ -> ^p+ K- pi+) pi-]CC",
+                "Lc_K1":    "[Lambda_b0 -> (Lambda_c+ -> p+ ^K- pi+) pi-]CC",
+                "Lc_Pi1": "[Lambda_b0 -> (Lambda_c+ -> p+ K- ^pi+) pi-]CC",
+                "Lb_Pi1": "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) ^pi-]CC"
+            },
+            ["Lb", "Lc"],
+            ["Lc_P1", "Lc_K1", "Lc_Pi1", "Lb_Pi1"],
+            "LbToLcPim",
+            ["Lambda_c+", "Lc"],
+        ),
+
+        # Lb -> Lc+ pi+ pi- pi-, with Lc+ -> p+ K- pi+
+        (
+            {
+                "Lb":   "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) pi+ pi- pi-]CC",
+                "Lc":   "[Lambda_b0 -> ^(Lambda_c+ -> p+ K- pi+) pi+ pi- pi-]CC",
+                "Lc_P1":    "[Lambda_b0 -> (Lambda_c+ -> ^p+ K- pi+) pi+ pi- pi-]CC",
+                "Lc_K1":    "[Lambda_b0 -> (Lambda_c+ -> p+ ^K- pi+) pi+ pi- pi-]CC",
+                "Lc_Pi1": "[Lambda_b0 -> (Lambda_c+ -> p+ K- ^pi+) pi+ pi- pi-]CC",
+                "Lb_Pi1": "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) ^pi+ pi- pi-]CC",
+                "Lb_Pi2": "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) pi+ ^pi- pi-]CC",
+                "Lb_Pi3": "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) pi+ pi- ^pi-]CC"
+            },
+            ["Lb", "Lc"],
+            ["Lc_P1", "Lc_K1", "Lc_Pi1", "Lb_Pi1", "Lb_Pi2", "Lb_Pi3"],
+            "LbToLcPipPimPim",
+            ["Lambda_c+", "Lc"],
+        )
+    ]
+     
+    line_name = 'SpruceBandQ_LbForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def Xibm():
+    
+    # XibmToJpsiXi = make_XibmToJpsiXi(process)
+    # XibmToJpsiPKK = make_XibmToJpsiPKK(process)
+
+    # XibmToXic0Pim = make_XibmToXic0Pim(process)
+    # XibmToLcKmPim = make_XibmToLcKmPim(process)
+    
+    decays = [
+        # Xibm -> J/psi Xi-, with Xi- -> Lambda0 pi-, Lambda0 -> p pi-
+         (
+             {
+                 "Xibm":  "[Xi_b- -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-)]CC",
+                 "Jpsi":  "[Xi_b- -> ^(J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-)]CC",
+                 "Jpsi_mup":   "[Xi_b- -> (J/psi(1S) -> ^mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-)]CC",
+                 "Jpsi_mum":   "[Xi_b- -> (J/psi(1S) -> mu+ ^mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-)]CC",
+                 "Xi":    "[Xi_b- -> (J/psi(1S) -> mu+ mu-) ^(Xi- -> (Lambda0 -> p+ pi-) pi-)]CC",
+                 "Lambda": "[Xi_b- -> (J/psi(1S) -> mu+ mu-) (Xi- -> ^(Lambda0 -> p+ pi-) pi-)]CC",
+                 "Lambda_P1":     "[Xi_b- -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> ^p+ pi-) pi-)]CC",
+                 "Lambda_Pi1":   "[Xi_b- -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ ^pi-) pi-)]CC",
+                 "Xi_Pi1":  "[Xi_b- -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) ^pi-)]CC"
+             },
+             ["Xibm", "Jpsi", "Xi", "Lambda"],
+             ["Jpsi_mup", "Jpsi_mum", "Lambda_P1", "Lambda_Pi1", "Xi_Pi1"],
+             "XibmToJpsiXi",
+            ["J/psi(1S)", "Jpsi"],
+         ),
+
+        # Xibm -> J/psi p+ K- K- decay
+        (
+            {
+                "Xibm":  "[Xi_b- -> (J/psi(1S) -> mu+ mu-) p+ K- K-]CC",
+                "Jpsi":  "[Xi_b- -> ^(J/psi(1S) -> mu+ mu-) p+ K- K-]CC",
+                "Jpsi_mup":   "[Xi_b- -> (J/psi(1S) -> ^mu+ mu-) p+ K- K-]CC",
+                "Jpsi_mum":   "[Xi_b- -> (J/psi(1S) -> mu+ ^mu-) p+ K- K-]CC",
+                "Xibm_P1":     "[Xi_b- -> (J/psi(1S) -> mu+ mu-) ^p+ K- K-]CC",
+                "Xibm_K1":    "[Xi_b- -> (J/psi(1S) -> mu+ mu-) p+ ^K- K-]CC",
+                "Xibm_K2":    "[Xi_b- -> (J/psi(1S) -> mu+ mu-) p+ K- ^K-]CC"
+            },
+            ["Xibm", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Xibm_P1", "Xibm_K1", "Xibm_K2"],
+            "XibmToJpsiPKK",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+
+        # Xibm -> Xic0 pi-, with Xic0 -> p+ K- K- pi+
+        (
+            {
+                "Xibm":  "[Xi_b- -> (Xi_c0 -> p+ K- K- pi+) pi-]CC",
+                "Xic0":  "[Xi_b- -> ^(Xi_c0 -> p+ K- K- pi+) pi-]CC",
+                "Xic0_P1":     "[Xi_b- -> (Xi_c0 -> ^p+ K- K- pi+) pi-]CC",
+                "Xic0_K1":    "[Xi_b- -> (Xi_c0 -> p+ ^K- K- pi+) pi-]CC",
+                "Xic0_K2":    "[Xi_b- -> (Xi_c0 -> p+ K- ^K- pi+) pi-]CC",
+                "Xic0_Pi1": "[Xi_b- -> (Xi_c0 -> p+ K- K- ^pi+) pi-]CC",
+                "Xibm_Pi1":  "[Xi_b- -> (Xi_c0 -> p+ K- K- pi+) ^pi-]CC"
+            },
+            ["Xibm", "Xic0"],
+            ["Xic0_P1", "Xic0_K1", "Xic0_K2", "Xic0_Pi1", "Xibm_Pi1"],
+            "XibmToXic0Pim",
+            ["Xi_c0", "Xic0"],
+        ),
+
+        # Xibm -> Lambda_c+ K- pi-, with Lambda_c+ -> p+ K- pi+
+        (
+            {
+                "Xibm":   "[Xi_b- -> (Lambda_c+ -> p+ K- pi+) K- pi-]CC",
+                "Lc":    "[Xi_b- -> ^(Lambda_c+ -> p+ K- pi+) K- pi-]CC",
+                "Lc_P1":      "[Xi_b- -> (Lambda_c+ -> ^p+ K- pi+) K- pi-]CC",
+                "Lc_K1":    "[Xi_b- -> (Lambda_c+ -> p+ ^K- pi+) K- pi-]CC",
+                "Lc_Pi1":   "[Xi_b- -> (Lambda_c+ -> p+ K- ^pi+) K- pi-]CC",
+                "Xibm_K1":     "[Xi_b- -> (Lambda_c+ -> p+ K- pi+) ^K- pi-]CC",
+                "Xibm_Pi1":   "[Xi_b- -> (Lambda_c+ -> p+ K- pi+) K- ^pi-]CC"
+            },
+            ["Xibm", "Lc"],
+            ["Lc_P1", "Lc_K1", "Lc_Pi1", "Xibm_K1", "Xibm_Pi1"],
+            "XibmToLcKmPim",
+            ["Lambda_c+", "Lc"],
+        )
+    ]
+
+     
+    line_name = 'SpruceBandQ_XibmForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def Xib0():
+    
+    # Xib0ToJpsiXiPi = make_Xib0ToJpsiXiPi(process)
+    # Xib0ToJpsiPKKPi = make_Xib0ToJpsiPKKPi(process)
+
+    # Xib0ToXicpPim = make_Xib0ToXicpPim(process)
+    # Xib0ToLcKmPipPim = make_Xib0ToLcKmPipPim(process)
+    
+    decays = [
+        # Xib0 -> J/psi Xi- pi+, with Xi- -> Lambda0 pi-, Lambda0 -> p+ pi-
+         (
+             {
+                 "Xib0":   "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-) pi+]CC",
+                 "Jpsi":   "[Xi_b0 -> ^(J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-) pi+]CC",
+                 "Jpsi_mup":    "[Xi_b0 -> (J/psi(1S) -> ^mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-) pi+]CC",
+                 "Jpsi_mum":    "[Xi_b0 -> (J/psi(1S) -> mu+ ^mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-) pi+]CC",
+                 "Xi":     "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) ^(Xi- -> (Lambda0 -> p+ pi-) pi-) pi+]CC",
+                 "Lambda": "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) (Xi- -> ^(Lambda0 -> p+ pi-) pi-) pi+]CC",
+                 "Lambda_P1":      "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> ^p+ pi-) pi-) pi+]CC",
+                 "Lambda_Pi1":    "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ ^pi-) pi-) pi+]CC",
+                 "Xi_Pi1":   "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) ^pi-) pi+]CC",
+                 "Xib0_Pi1":   "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) (Xi- -> (Lambda0 -> p+ pi-) pi-) ^pi+]CC"
+             },
+             ["Xib0", "Jpsi", "Xi", "Lambda"],
+             ["Jpsi_mup", "Jpsi_mum", "Lambda_P1", "Lambda_Pi1", "Xi_Pi1", "Xib0_Pi1"],
+             "Xib0ToJpsiXiPi", 
+            ["J/psi(1S)", "Jpsi"],
+         ),
+
+        # Xib0 -> J/psi p+ K- K- pi+
+        (
+            {
+                "Xib0":   "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) p+ K- K- pi+]CC",
+                "Jpsi":   "[Xi_b0 -> ^(J/psi(1S) -> mu+ mu-) p+ K- K- pi+]CC",
+                "Jpsi_mup":    "[Xi_b0 -> (J/psi(1S) -> ^mu+ mu-) p+ K- K- pi+]CC",
+                "Jpsi_mum":    "[Xi_b0 -> (J/psi(1S) -> mu+ ^mu-) p+ K- K- pi+]CC",
+                "Xib0_P1":      "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) ^p+ K- K- pi+]CC",
+                "Xib0_K1":     "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) p+ ^K- K- pi+]CC",
+                "Xib0_K2":     "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) p+ K- ^K- pi+]CC",
+                "Xib0_Pi1":    "[Xi_b0 -> (J/psi(1S) -> mu+ mu-) p+ K- K- ^pi+]CC"
+            },
+            ["Xib0", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Xib0_P1", "Xib0_K1", "Xib0_K2", "Xib0_Pi1"],
+            "Xib0ToJpsiPKKPi", 
+            ["J/psi(1S)", "Jpsi"],
+        ),
+
+        # Xib0 -> Xic+ pi-, with Xic+ -> p+ K- pi+
+        (
+            {
+                "Xib0":   "[Xi_b0 -> (Xi_c+ -> p+ K- pi+) pi-]CC",
+                "Xicp":   "[Xi_b0 -> ^(Xi_c+ -> p+ K- pi+) pi-]CC",
+                "Xicp_P1":      "[Xi_b0 -> (Xi_c+ -> ^p+ K- pi+) pi-]CC",
+                "Xicp_K1":      "[Xi_b0 -> (Xi_c+ -> p+ ^K- pi+) pi-]CC",
+                "Xicp_Pi1":  "[Xi_b0 -> (Xi_c+ -> p+ K- ^pi+) pi-]CC",
+                "Xib0_Pi1":   "[Xi_b0 -> (Xi_c+ -> p+ K- pi+) ^pi-]CC"
+            },
+            ["Xib0", "Xicp"],
+            ["Xicp_P1", "Xicp_K1", "Xicp_Pi1", "Xib0_Pi1"],
+            "Xib0ToXicpPim",
+            ["Xi_c+", "Xicp"],
+        ),
+
+        # Xib0 -> Lambda_c+ K- pi+ pi-, with Lambda_c+ -> p+ K- pi+
+        (
+            {
+                "Xib0":   "[Xi_b0 -> (Lambda_c+ -> p+ K- pi+) K- pi+ pi-]CC",
+                "Lc":    "[Xi_b0 -> ^(Lambda_c+ -> p+ K- pi+) K- pi+ pi-]CC",
+                "Lc_P1":      "[Xi_b0 -> (Lambda_c+ -> ^p+ K- pi+) K- pi+ pi-]CC",
+                "Lc_K1":    "[Xi_b0 -> (Lambda_c+ -> p+ ^K- pi+) K- pi+ pi-]CC",
+                "Lc_Pi1":   "[Xi_b0 -> (Lambda_c+ -> p+ K- ^pi+) K- pi+ pi-]CC",
+                "Xib0_K1":     "[Xi_b0 -> (Lambda_c+ -> p+ K- pi+) ^K- pi+ pi-]CC",
+                "Xib0_Pi1":  "[Xi_b0 -> (Lambda_c+ -> p+ K- pi+) K- ^pi+ pi-]CC",
+                "Xib0_Pi2":  "[Xi_b0 -> (Lambda_c+ -> p+ K- pi+) K- pi+ ^pi-]CC"
+            },
+            ["Xib0", "Lc"],
+            ["Lc_P1", "Lc_K1", "Lc_Pi1", "Xib0_K1", "Xib0_Pi1", "Xib0_Pi2"],
+            "Xib0ToLcKmPipPim",
+            ["Lambda_c+", "Lc"],
+        )
+    ]
+    
+     
+    line_name = 'SpruceBandQ_Xib0ForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain )]
+    
+    return algs
+
+
+def Omegab():
+    
+    # OmegabToJpsiOmega = make_OmegabToJpsiOmega(process)
+    # OmegabToJpsiPKKKPi = make_OmegabToJpsiPKKKPi(process)
+    # OmbToOmcPim = make_OmbToOmcPim(process)
+    # OmbToXicpKmPim = make_OmbToXicpKmPim(process)
+    # OmbToLcpKmKmPipPim = make_OmbToLcpKmKmPipPim(process)
+    
+    decays = [
+        # Omegab -> J/psi Omega-, with Omega- -> (Lambda0 -> p+ pi-) K-
+         (
+             {
+                 "Omegab":  "[Omega_b- -> (J/psi(1S) -> mu+ mu-) (Omega- -> (Lambda0 -> p+ pi-) K-)]CC",
+                 "Jpsi":    "[Omega_b- -> ^(J/psi(1S) -> mu+ mu-) (Omega- -> (Lambda0 -> p+ pi-) K-)]CC",
+                 "Jpsi_mup":     "[Omega_b- -> (J/psi(1S) -> ^mu+ mu-) (Omega- -> (Lambda0 -> p+ pi-) K-)]CC",
+                 "Jpsi_mum":     "[Omega_b- -> (J/psi(1S) -> mu+ ^mu-) (Omega- -> (Lambda0 -> p+ pi-) K-)]CC",
+                 "Omega":   "[Omega_b- -> (J/psi(1S) -> mu+ mu-) ^(Omega- -> (Lambda0 -> p+ pi-) K-)]CC",
+                 "Lambda":  "[Omega_b- -> (J/psi(1S) -> mu+ mu-) (Omega- -> ^(Lambda0 -> p+ pi-) K-)]CC",
+                 "Lambda_P1":       "[Omega_b- -> (J/psi(1S) -> mu+ mu-) (Omega- -> (Lambda0 -> ^p+ pi-) K-)]CC",
+                 "Lambda_Pi1":     "[Omega_b- -> (J/psi(1S) -> mu+ mu-) (Omega- -> (Lambda0 -> p+ ^pi-) K-)]CC",
+                 "Omega_K1":       "[Omega_b- -> (J/psi(1S) -> mu+ mu-) (Omega- -> (Lambda0 -> p+ pi-) ^K-)]CC"
+             },
+             ["Omegab", "Jpsi", "Omega", "Lambda"],
+             ["Jpsi_mup", "Jpsi_mum", "Lambda_P1", "Lambda_Pi1", "Omega_K1"],
+             "OmegabToJpsiOmega", 
+            ["J/psi(1S)", "Jpsi"],
+         ),
+
+        # Omegab -> J/psi p+ K- K- K- pi+
+        (
+            {
+                "Omegab":  "[Omega_b- -> (J/psi(1S) -> mu+ mu-) p+ K- K- K- pi+]CC",
+                "Jpsi":    "[Omega_b- -> ^(J/psi(1S) -> mu+ mu-) p+ K- K- K- pi+]CC",
+                "Jpsi_mup":     "[Omega_b- -> (J/psi(1S) -> ^mu+ mu-) p+ K- K- K- pi+]CC",
+                "Jpsi_mum":     "[Omega_b- -> (J/psi(1S) -> mu+ ^mu-) p+ K- K- K- pi+]CC",
+                "Omegab_P1":       "[Omega_b- -> (J/psi(1S) -> mu+ mu-) ^p+ K- K- K- pi+]CC",
+                "Omegab_K1":      "[Omega_b- -> (J/psi(1S) -> mu+ mu-) p+ ^K- K- K- pi+]CC",
+                "Omegab_K2":      "[Omega_b- -> (J/psi(1S) -> mu+ mu-) p+ K- ^K- K- pi+]CC",
+                "Omegab_K3":      "[Omega_b- -> (J/psi(1S) -> mu+ mu-) p+ K- K- ^K- pi+]CC",
+                "Omegab_Pi1":     "[Omega_b- -> (J/psi(1S) -> mu+ mu-) p+ K- K- K- ^pi+]CC"
+            },
+            ["Omegab", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Omegab_P1", "Omegab_K1", "Omegab_K2", "Omegab_K3", "Omegab_Pi1"],
+            "OmegabToJpsiPKKKPi",
+            ["J/psi(1S)", "Jpsi"],
+        ),
+
+        # Omb -> Omega_c0 pi-, with Omega_c0 -> p+ K- K- pi+
+        (
+            {
+                "Omegab":     "[Omega_b- -> (Omega_c0 -> p+ K- K- pi+) pi-]CC",
+                "Omegac":     "[Omega_b- -> ^(Omega_c0 -> p+ K- K- pi+) pi-]CC",
+                "Omegac_P1":       "[Omega_b- -> (Omega_c0 -> ^p+ K- K- pi+) pi-]CC",
+                "Omegac_K1":      "[Omega_b- -> (Omega_c0 -> p+ ^K- K- pi+) pi-]CC",
+                "Omegac_K2":      "[Omega_b- -> (Omega_c0 -> p+ K- ^K- pi+) pi-]CC",
+                "Omegac_Pi1":   "[Omega_b- -> (Omega_c0 -> p+ K- K- ^pi+) pi-]CC",
+                "Omegab_Pi1":    "[Omega_b- -> (Omega_c0 -> p+ K- K- pi+) ^pi-]CC"
+            },
+            ["Omegab", "Omegac"],
+            ["Omegac_P1", "Omegac_K1", "Omegac_K2", "Omegac_Pi1", "Omegab_Pi1"],
+            "OmegabToOmcPim",
+            ["Omega_c0", "Omegac"],
+        ),
+
+        # Omb -> Xic+ K- pi-, with Xic+ -> p+ K- pi+
+        (
+            {
+                "Omegab":     "[Omega_b- -> (Xi_c+ -> p+ K- pi+) K- pi-]CC",
+                "Xic":     "[Omega_b- -> ^(Xi_c+ -> p+ K- pi+) K- pi-]CC",
+                "Xic_P1":       "[Omega_b- -> (Xi_c+ -> ^p+ K- pi+) K- pi-]CC",
+                "Xic_K1":      "[Omega_b- -> (Xi_c+ -> p+ ^K- pi+) K- pi-]CC",
+                "Xic_Pi1":   "[Omega_b- -> (Xi_c+ -> p+ K- ^pi+) K- pi-]CC",
+                "Omegab_K1":      "[Omega_b- -> (Xi_c+ -> p+ K- pi+) ^K- pi-]CC",
+                "Omegab_Pi1":    "[Omega_b- -> (Xi_c+ -> p+ K- pi+) K- ^pi-]CC"
+            },
+            ["Omegab", "Xic"],
+            ["Xic_P1", "Xic_K1", "Xic_Pi1", "Omegab_K1", "Omegab_Pi1"],
+            "OmegabToXicpKmPim",
+            ["Xi_c+", "Xic"],
+        ),
+
+        # Omb -> Lambda_c+ K- K- pi+ pi-, with Lambda_c+ -> p+ K- pi+
+        (
+            {
+                "Omegab":     "[Omega_b- -> (Lambda_c+ -> p+ K- pi+) K- K- pi+ pi-]CC",
+                "Lc":      "[Omega_b- -> ^(Lambda_c+ -> p+ K- pi+) K- K- pi+ pi-]CC",
+                "Lc_P1":       "[Omega_b- -> (Lambda_c+ -> ^p+ K- pi+) K- K- pi+ pi-]CC",
+                "Lc_K1":      "[Omega_b- -> (Lambda_c+ -> p+ ^K- pi+) K- K- pi+ pi-]CC",
+                "Lc_Pi1":    "[Omega_b- -> (Lambda_c+ -> p+ K- ^pi+) K- K- pi+ pi-]CC",
+                "Omegab_K1":      "[Omega_b- -> (Lambda_c+ -> p+ K- pi+) ^K- K- pi+ pi-]CC",
+                "Omegab_K2":   "[Omega_b- -> (Lambda_c+ -> p+ K- pi+) K- ^K- pi+ pi-]CC",
+                "Omegab_Pi1":   "[Omega_b- -> (Lambda_c+ -> p+ K- pi+) K- K- ^pi+ pi-]CC",
+                "Omegab_Pi2":   "[Omega_b- -> (Lambda_c+ -> p+ K- pi+) K- K- pi+ ^pi-]CC"
+            },
+            ["Omegab", "Lc"],
+            ["Lc_P1", "Lc_K1", "Lc_Pi1", "Omegab_K1", "Omegab_K2", "Omegab_Pi1", "Omegab_Pi2"],
+            "OmegabToLcpKmKmPipPim",
+            ["Lambda_c+", "Lc"],
+        )
+    ]
+    
+    
+    line_name = 'SpruceBandQ_OmegabForSpectroscopy'
+    HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+    my_filter = line_prefilter(HLT2_decision)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+def SL():
+    
+    decays = [
+        # Bu -> D0 mu+ decay, with D0 -> K- pi+
+        (
+            {
+                "Bu":   "[B+ -> (D~0 -> K+ pi-) mu+]CC",
+                "D0":   "[B+ -> ^(D~0 -> K+ pi-) mu+]CC",
+                "D0_K1":    "[B+ -> (D~0 -> ^K+ pi-) mu+]CC",
+                "D0_Pi1": "[B+ -> (D~0 -> K+ ^pi-) mu+]CC",
+                "D0_mu1"  : "[B+ -> (D~0 -> K+ pi-) ^mu+]CC"
+            },
+            ["Bu", "D0"],
+            ["D0_K1", "D0_Pi1", "D0_mu1"],
+            "BuToD0mu",
+            ["D~0", "D0"],
+        ),
+        # Bd -> D+ mu- decay, with D+ -> K- pi+ pi+
+        (
+            {
+                "Bd":   "[B0 -> (D- -> K+ pi- pi-) mu+]CC",
+                "Dm":   "[B0 -> ^(D- -> K+ pi- pi-) mu+]CC",
+                "Dm_K1":    "[B0 -> (D- -> ^K+ pi- pi-) mu+]CC",
+                "Dm_Pi1": "[B0 -> (D- -> K+ ^pi- pi-) mu+]CC",
+                "Dm_Pi2": "[B0 -> (D- -> K+ pi- ^pi-) mu+]CC",
+                "Bd_mu1"  :  "[B0 -> (D- -> K+ pi- pi-) ^mu+]CC"
+            },
+            ["Bd", "Dm"],
+            ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bd_mu1"],
+            "BdToDmmu",
+            ["D-", "Dm"],
+        ),
+        # Bs -> D_s- mu+ decay (corrected)
+        (
+            {
+                "Bs":   "[B_s0 -> (D_s- -> K- K+ pi-) mu+]CC",
+                "Ds": "[B_s0 -> ^(D_s- -> K- K+ pi-) mu+]CC",
+                "Ds_K1":   "[B_s0 -> (D_s- -> ^K- K+ pi-) mu+]CC",
+                "Ds_K2":   "[B_s0 -> (D_s- -> K- ^K+ pi-) mu+]CC",
+                "Ds_Pi1": "[B_s0 -> (D_s- -> K- K+ ^pi-) mu+]CC",
+                "Bs_mu1": "[B_s0 -> (D_s- -> K- K+ pi-) ^mu+]CC"
+            },
+            ["Bs", "Ds"],
+            ["Ds_K1", "Ds_K2", "Ds_Pi1", "Bs_mu1"],
+            "BsToDsmmu",
+            ["D_s-", "Ds"],
+        ),
+        # Bc -> J/psi mu+ decay
+        (
+            {
+                "Bc":   "[B_c+ -> (J/psi(1S) -> mu+ mu-) mu+]CC",
+                "Jpsi": "[B_c+ -> ^(J/psi(1S) -> mu+ mu-) mu+]CC",
+                "Jpsi_mup":  "[B_c+ -> (J/psi(1S) -> ^mu+ mu-) mu+]CC",
+                "Jpsi_mum":  "[B_c+ -> (J/psi(1S) -> mu+ ^mu-) mu+]CC",
+                "Bc_mu1":   "[B_c+ -> (J/psi(1S) -> mu+ mu-) ^mu+]CC"
+            },
+            ["Bc", "Jpsi"],
+            ["Jpsi_mup", "Jpsi_mum", "Bc_mu1"],
+            "BcToJpsimu", 
+            ["J/psi(1S)", "Jpsi"],
+        ),
+        # Lb -> Lc+ mu- decay, with Lc+ -> p+ K- pi+
+        (
+            {
+                "Lb":   "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) mu-]CC",
+                "Lc":   "[Lambda_b0 -> ^(Lambda_c+ -> p+ K- pi+) mu-]CC",
+                "Lc_P1":    "[Lambda_b0 -> (Lambda_c+ -> ^p+ K- pi+) mu-]CC",
+                "Lc_K1":    "[Lambda_b0 -> (Lambda_c+ -> p+ ^K- pi+) mu-]CC",
+                "Lc_Pi1": "[Lambda_b0 -> (Lambda_c+ -> p+ K- ^pi+) mu-]CC",
+                "Lb_mu1"  : "[Lambda_b0 -> (Lambda_c+ -> p+ K- pi+) ^mu-]CC"
+            },
+            ["Lb", "Lc"],
+            ["Lc_P1", "Lc_K1", "Lc_Pi1", "Lb_mu1"],
+            "LbToLcmu",
+            ["Lambda_c+", "Lc"],
+        ),
+        # Xibm -> Xic0 mu-, with Xic0 -> p+ K- K- pi+
+        (
+            {
+                "Xibm":  "[Xi_b- -> (Xi_c0 -> p+ K- K- pi+) mu-]CC",
+                "Xic0":  "[Xi_b- -> ^(Xi_c0 -> p+ K- K- pi+) mu-]CC",
+                "Xic0_P1":     "[Xi_b- -> (Xi_c0 -> ^p+ K- K- pi+) mu-]CC",
+                "Xic0_K1":    "[Xi_b- -> (Xi_c0 -> p+ ^K- K- pi+) mu-]CC",
+                "Xic0_K2":    "[Xi_b- -> (Xi_c0 -> p+ K- ^K- pi+) mu-]CC",
+                "Xic0_Pi1": "[Xi_b- -> (Xi_c0 -> p+ K- K- ^pi+) mu-]CC",
+                "Xibm_mu1"  :  "[Xi_b- -> (Xi_c0 -> p+ K- K- pi+) ^mu-]CC"
+            },
+            ["Xibm", "Xic0"],
+            ["Xic0_P1", "Xic0_K1", "Xic0_K2", "Xic0_Pi1", "Xibm_mu1"],
+            "XibmToXic0mu",
+            ["Xi_c0", "Xic0"],
+        ),
+        # Xib0 -> Xic+ mu-, with Xic+ -> p+ K- pi+
+        (
+            {
+                "Xib0":   "[Xi_b0 -> (Xi_c+ -> p+ K- pi+) mu-]CC",
+                "Xicp":   "[Xi_b0 -> ^(Xi_c+ -> p+ K- pi+) mu-]CC",
+                "Xicp_P1":      "[Xi_b0 -> (Xi_c+ -> ^p+ K- pi+) mu-]CC",
+                "Xicp_K1":      "[Xi_b0 -> (Xi_c+ -> p+ ^K- pi+) mu-]CC",
+                "Xicp_Pi1":  "[Xi_b0 -> (Xi_c+ -> p+ K- ^pi+) mu-]CC",
+                "Xib0_mu1"  :   "[Xi_b0 -> (Xi_c+ -> p+ K- pi+) ^mu-]CC"
+            },
+            ["Xib0", "Xicp"],
+            ["Xicp_P1", "Xicp_K1", "Xicp_Pi1", "Xib0_mu1"],
+            "Xib0ToXicpmu",
+            ["Xi_c+", "Xicp"],
+        ),
+        # Omb -> Omega_c0 mu-, with Omega_c0 -> p+ K- K- pi+
+        (
+            {
+                "Omegab":  "[Omega_b- -> (Omega_c0 -> p+ K- K- pi+) mu-]CC",
+                "Omegac":     "[Omega_b- -> ^(Omega_c0 -> p+ K- K- pi+) mu-]CC",
+                "Omegac_P1":       "[Omega_b- -> (Omega_c0 -> ^p+ K- K- pi+) mu-]CC",
+                "Omegac_K1":      "[Omega_b- -> (Omega_c0 -> p+ ^K- K- pi+) mu-]CC",
+                "Omegac_K2":      "[Omega_b- -> (Omega_c0 -> p+ K- ^K- pi+) mu-]CC",
+                "Omegac_Pi1":   "[Omega_b- -> (Omega_c0 -> p+ K- K- ^pi+) mu-]CC",
+                "Omegab_mu1":    "[Omega_b- -> (Omega_c0 -> p+ K- K- pi+) ^mu-]CC"
+            },
+            ["Omegab", "Omegac"],
+            ["Omegac_P1", "Omegac_K1", "Omegac_K2", "Omegac_Pi1", "Omegab_mu1"],
+            "OmegabToOmcmu",
+            ["Omega_c0", "Omegac"],
+        ),
+    ]
+    
+    # line_name = 'SpruceBandQ_AllBForSpectroscopySL'
+    # my_filter = line_prefilter(line_name)
+    
+    algs = {} #[my_filter]
+    
+    for decay_config in decays:
+        
+        decay_descriptor, composite_list, stable_list, decay_name, ParticleToConstrain = decay_config
+        
+        _name = composite_list[0]
+        if _name == "Bu" or _name == "Bd": _name="Bud"
+        if _name == "Xib0" or _name == "Xibm": _name="Xib"
+        
+        line_name = f'SpruceBandQ_{_name}ForSpectroscopySL'
+        HLT2_decision = line_name.replace("Spruce", "Hlt2") + "FullDecision"
+        my_filter = line_prefilter(HLT2_decision)
+        
+        algs[decay_name] = [my_filter, template(decay_descriptor, line_name, False, HLT2_lines+[HLT2_decision], composite_list, stable_list, decay_name, ParticleToConstrain)]
+    
+    return algs
+
+
+# def entry_point(options: Options):
+    
+#     algs_dict = Bu()
+#     algs_dict.update(Bd())
+#     algs_dict.update(Bs())
+#     algs_dict.update(Bc())
+#     algs_dict.update(Lb())
+#     algs_dict.update(Xibm())
+#     algs_dict.update(Xib0())
+#     algs_dict.update(Omegab())
+    
+#     algs_dict.update(SL())
+    
+#     return make_config(options, algs_dict)
diff --git a/bandq_resprucing/BforSpectroscopy/make_dtf.py b/bandq_resprucing/BforSpectroscopy/make_dtf.py
new file mode 100644
index 0000000000..16fc87e84f
--- /dev/null
+++ b/bandq_resprucing/BforSpectroscopy/make_dtf.py
@@ -0,0 +1,228 @@
+import Functors as F
+from Functors.math import log
+
+from FunTuple import FunctorCollection
+from FunTuple.functorcollections import (
+    MCHierarchy,
+    MCPromptDecay,
+    Kinematics,
+    SelectionInfo,
+    HltTisTos,
+    MCVertexInfo,
+    MCKinematics,
+    ParticleID, #wrong variables PID_PI = 0, PROBNN_D = nan
+    EventInfo,
+    LHCInfo,
+    ParticleIsolation,
+    MCPrimaries,
+    MCReconstructed,
+    MCReconstructible,
+)
+
+from DaVinciMCTools import MCTruthAndBkgCat, MCReconstructed, MCReconstructible
+from PyConf.Algorithms import ParticleToSubcombinationsAlg
+from DecayTreeFitter import DecayTreeFitter
+
+from DaVinci.algorithms import create_lines_filter
+from PyConf.reading import get_particles
+from FunTuple import FunTuple_Particles as Funtuple
+
+
+
+def make_basic_dtf_variables(pvs, data, DTF=None, pv_constraint=False, mass_constraint=False, particle_name=""):
+    variables = (
+        FunctorCollection(
+            {
+                "TRCHI2DOF": F.CHI2DOF @ F.TRACK,
+                "ETA": F.ETA,
+                "PHI": F.PHI,
+                "TRGHOSTPROB": F.GHOSTPROB,
+                "BPVIPCHI2": F.BPVIPCHI2(pvs),
+                "BPVIP": F.BPVIP(pvs),
+                "BPVX": F.BPVX(pvs),
+                "BPVY": F.BPVY(pvs),
+                "BPVZ": F.BPVZ(pvs),
+                "TX"          : F.TX,
+                "TY"          : F.TY,
+                "KEY"         : F.VALUE_OR(-1) @ F.OBJECT_KEY @ F.TRACK,
+#                "CTB"         : F.POSITION @ F.CLOSESTTOBEAM @ F.TRACK,
+                "TRACKPT": F.TRACK_PT,
+                "TRACKHISTORY": F.VALUE_OR(-1) @ F.TRACKHISTORY @ F.TRACK,
+                "QOVERP": F.QOVERP @ F.TRACK,
+                "NDOF": F.VALUE_OR(-1) @ F.NDOF @ F.TRACK,
+            }
+        )
+        + Kinematics()
+    )
+
+    if(mass_constraint):
+        if(pv_constraint): # MASS + PV
+            dtf_variables_mass_pv = FunctorCollection({
+                        'DTF_PV_M'+ particle_name + '_' + k: DTF(v)
+                        for k, v in variables.get_thor_functors().items()
+                    })
+            return dtf_variables_mass_pv
+        else: # MASS
+            dtf_variables_mass = FunctorCollection(
+                {'DTF_M'+ particle_name + '_' + k: DTF(v)
+                 for k, v in variables.get_thor_functors().items()})
+        return dtf_variables_mass
+
+    elif(pv_constraint): # PV
+        dtf_variables_pv = FunctorCollection({
+                'DTF_PV_' + k: DTF(v)
+                for k, v in variables.get_thor_functors().items()
+            })
+        return dtf_variables_pv
+
+    else: # NO MASS/PV
+        dtf_variables = FunctorCollection(
+            {'DTF_' + k: DTF(v)
+             for k, v in variables.get_thor_functors().items()})
+        return dtf_variables
+
+
+def make_composite_dtf_variables(pvs, data, DTF=None, pv_constraint=False, mass_constraint=False, particle_name=""):
+    variables = (
+        FunctorCollection(
+            {
+                "BPVDIRA": F.BPVDIRA(pvs),
+                "VCHI2DOF": F.CHI2DOF, #CHI2VXNDOF
+                "BPVFDCHI2": F.BPVFDCHI2(pvs),
+                "BPVFD": F.BPVFD(pvs),
+                "BPVVDRHO": F.BPVVDRHO(pvs),
+                "BPVVDZ": F.BPVVDZ(pvs),
+                "BPVIPCHI2": F.BPVIPCHI2(pvs),
+                "BPVIP": F.BPVIP(pvs),
+                "BPVLTIME": F.BPVLTIME(pvs),
+                "ETA": F.ETA,
+                "PHI": F.PHI,
+                "END_VX": F.END_VX, #END_
+                "END_VY": F.END_VY,
+                "END_VZ": F.END_VZ,
+                "BPVX": F.BPVX(pvs),
+                "BPVY": F.BPVY(pvs),
+                "BPVZ": F.BPVZ(pvs),
+                "ALLPVFD"     : F.ALLPV_FD(pvs),
+                "ALLPVIP"     : F.ALLPV_IP(pvs),
+                
+            }
+        )
+        + Kinematics()
+    )
+
+    addstring = "DTF"
+    if(pv_constraint):
+            addstring += '_PV'
+    if(mass_constraint):
+            addstring += '_M'
+    addstring += particle_name
+
+    DTF_chi2ndof = FunctorCollection(
+            {
+                addstring+"_DTFCHI2": DTF.CHI2,
+                addstring+"_DTFNDOF": DTF.NDOF,
+                addstring+"_CTAU": DTF.CTAU,
+                addstring+"_CTAUERR": DTF.CTAUERR,
+                addstring+"_MERR": DTF.MASSERR,
+            }
+    )
+
+    if(mass_constraint):
+        if(pv_constraint): # MASS + PV
+            dtf_variables_mass_pv = FunctorCollection({
+                        'DTF_PV_M'+ particle_name + '_' + k: DTF(v)
+                        for k, v in variables.get_thor_functors().items()
+                    })
+            return dtf_variables_mass_pv+DTF_chi2ndof
+        else: # MASS
+            dtf_variables_mass = FunctorCollection(
+                {'DTF_M'+ particle_name + '_' + k: DTF(v)
+                 for k, v in variables.get_thor_functors().items()})
+        return dtf_variables_mass+DTF_chi2ndof
+
+    elif(pv_constraint): # PV
+        dtf_variables_pv = FunctorCollection({
+                'DTF_PV_' + k: DTF(v)
+                for k, v in variables.get_thor_functors().items()
+            })
+        return dtf_variables_pv+DTF_chi2ndof
+
+    else: # NO MASS/PV
+        dtf_variables = FunctorCollection(
+            {'DTF_' + k: DTF(v)
+             for k, v in variables.get_thor_functors().items()})
+        return dtf_variables+DTF_chi2ndof
+
+
+
+def make_dtf_variables(pvs, input_data, ptype, ParticleToConstrain_PDGName, ParticleToConstrain_Name):
+
+    if ptype not in ["basic", "composite"]:
+        Exception(f"Need \'basic\' or \'composite\'. Got {ptype}")
+
+    from DecayTreeFitter import DecayTreeFitter
+    
+#    DTF = DecayTreeFitter(
+#        name=f'DTF_{{hash}}',
+#        input_particles=input_data)
+#
+#    DTFvtx = DecayTreeFitter(
+#        name=f'DTFvtx_{{hash}}',
+#        input_particles=input_data,
+#        input_pvs=pvs)
+
+    DTFmassCharm = DecayTreeFitter(
+        name=f'DTFmassCharm_{{hash}}',
+        input_particles=input_data,
+        mass_constraints=[ParticleToConstrain_PDGName])
+
+#    DTFvtxmassCharm = DecayTreeFitter(
+#        name=f'DTFvtxmassCharm_{{hash}}',
+#        input_particles=input_data,
+#        input_pvs=pvs,
+#        mass_constraints=["J/psi(1S)"])
+
+
+
+    if ptype == "basic":
+#        dtf_vars = make_basic_dtf_variables(pvs, input_data,
+#                                            DTF=DTF,
+#                                            pv_constraint=False,
+#                                            mass_constraint=False)
+#        dtf_vars += make_basic_dtf_variables(pvs, input_data,
+#                                            DTF=DTFvtx,
+#                                            pv_constraint=True,
+#                                            mass_constraint=False)
+        dtf_vars = make_basic_dtf_variables(pvs, input_data,
+                                             DTF=DTFmassCharm,
+                                             pv_constraint=False,
+                                             mass_constraint=True, particle_name=ParticleToConstrain_Name)
+#        dtf_vars += make_basic_dtf_variables(pvs, input_data,
+#                                             DTF=DTFvtxmassCharm,
+#                                             pv_constraint=True,
+#                                             mass_constraint=True, particle_name="Jpsi")
+#
+        return dtf_vars
+
+    if ptype == "composite":
+#        dtf_vars = make_composite_dtf_variables(pvs, input_data,
+#                                            DTF=DTF,
+#                                            pv_constraint=False,
+#                                            mass_constraint=False)
+#        dtf_vars += make_composite_dtf_variables(pvs, input_data,
+#                                            DTF=DTFvtx,
+#                                            pv_constraint=True,
+#                                            mass_constraint=False)
+        dtf_vars = make_composite_dtf_variables(pvs, input_data,
+                                             DTF=DTFmassCharm,
+                                             pv_constraint=False,
+                                             mass_constraint=True, particle_name=ParticleToConstrain_Name)
+#        dtf_vars += make_composite_dtf_variables(pvs, input_data,
+#                                             DTF=DTFvtxmassCharm,
+#                                             pv_constraint=True,
+#                                             mass_constraint=True, particle_name="Jpsi")
+#
+        return dtf_vars
+
+
diff --git a/bandq_resprucing/BforSpectroscopy/tupling_maker.py b/bandq_resprucing/BforSpectroscopy/tupling_maker.py
new file mode 100644
index 0000000000..b59ddced97
--- /dev/null
+++ b/bandq_resprucing/BforSpectroscopy/tupling_maker.py
@@ -0,0 +1,345 @@
+###############################################################################
+# (c) Copyright 2021-2022 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.                                       #
+###############################################################################
+"""
+Read an HLT2 file and create an ntuple with the new DaVinci configuration.
+"""
+import Functors as F
+import FunTuple.functorcollections as FC
+from FunTuple import FunctorCollection
+from FunTuple import FunTuple_Particles as Funtuple
+from PyConf.reading import get_particles, get_pvs, get_rec_summary
+from DaVinci.algorithms import create_lines_filter
+from DaVinciMCTools import MCTruthAndBkgCat
+from FunTuple.functorcollections import MCHierarchy, MCPrimaries, MCPromptDecay, Kinematics, SelectionInfo, HltTisTos, MCVertexInfo, MCKinematics, ParticleID, EventInfo
+from PyConf.reading import get_odin  # get_decreports,
+from DecayTreeFitter import DecayTreeFitter
+from Hlt2Conf.algorithms_thor import ParticleFilter
+
+
+
+from .make_dtf import make_dtf_variables
+
+
+_basic = "basic"
+_composite = "composite"
+_toplevel = "toplevel"
+
+def all_variables(pvs, mctruth, ptype, candidates=None, ftAlg=None):
+    """
+    function that returns dictionary of functors that work.
+    
+    functors are listed in order of https://lhcbdoc.web.cern.ch/lhcbdoc/moore/master/selection/thor_functors_reference.html#module-Functo
+    """
+    if ptype not in [_basic, _composite]:
+        Exception(f"I want {_basic} or {_composite}. Got {ptype}")
+    all_vars = FunctorCollection({})
+    
+    comp = _composite == ptype or _toplevel == ptype  # is composite
+    basic = _basic == ptype  # is not composite
+    top = _toplevel == ptype  # the B
+
+    # First import everything that comes in functorcollections
+    all_vars += FC.Kinematics()
+    if basic:
+        all_vars += FC.ParticleID(extra_info=True)
+    
+    # if comp:
+    #     all_vars.update({"ALV": F.ALV(Child1=1, Child2=2)})
+        
+    if comp:  # all these require a vertex
+        all_vars.update({"BPVCORRM": F.BPVCORRM(pvs)})
+        all_vars.update({"BPVCORRMERR": F.BPVCORRMERR(pvs)})
+        all_vars.update({"BPVDIRA": F.BPVDIRA(pvs)})
+        all_vars.update({"BPVDLS": F.BPVDLS(pvs)})
+        all_vars.update({"BPVETA": F.BPVETA(pvs)})
+        all_vars.update({"BPVFD": F.BPVFD(pvs)})
+        all_vars.update({"BPVFDCHI2": F.BPVFDCHI2(pvs)})
+        all_vars.update({"BPVFDIR": F.BPVFDIR(pvs)})
+        all_vars.update({"BPVFDVEC": F.BPVFDVEC(pvs)})
+
+    all_vars.update({"BPVIP": F.BPVIP(pvs)})
+    all_vars.update({"BPVIPCHI2": F.BPVIPCHI2(pvs)})
+    all_vars.update({"BPVX": F.BPVX(pvs)})
+    all_vars.update({"BPVY": F.BPVY(pvs)})
+    all_vars.update({"BPVZ": F.BPVZ(pvs)})
+
+    if comp:  # all these require a vertex
+        all_vars.update({"ALLPV_FD": F.ALLPV_FD(pvs)})
+        all_vars.update({"ALLPV_IP": F.ALLPV_IP(pvs)})
+        all_vars.update({"BPVLTIME": F.BPVLTIME(pvs)})
+        all_vars.update({"BPVVDRHO": F.BPVVDRHO(pvs)})
+        all_vars.update({"BPVVDX": F.BPVVDX(pvs)})
+        all_vars.update({"BPVVDY": F.BPVVDY(pvs)})
+        all_vars.update({"BPVVDZ": F.BPVVDZ(pvs)})
+    
+    all_vars.update({"CHARGE": F.CHARGE})
+    all_vars.update({"CHI2": F.CHI2})
+    all_vars.update({"CHI2DOF": F.CHI2DOF})
+
+    # if top:  # apply this only to B
+    #     all_vars.update({"CHILD1_PT": F.CHILD(1, F.PT)})  # example of CHILD
+    #     all_vars.update({"Ds_END_VZ": F.CHILD(1, F.END_VZ)})
+    #     all_vars.update({"Delta_END_VZ_DsB0": F.CHILD(1, F.END_VZ) - F.END_VZ})
+    
+    if comp:
+        # all_vars.update({"DOCA": F.SDOCA(Child1=1, Child2=2)})
+        # all_vars.update({"DOCACHI2": F.SDOCACHI2(Child1=1, Child2=2)})
+        all_vars.update({"END_VRHO": F.END_VRHO})
+        all_vars.update({"END_VX": F.END_VX})
+        all_vars.update({"END_VY": F.END_VY})
+        all_vars.update({"END_VZ": F.END_VZ})
+    
+    all_vars.update({"ETA": F.ETA})
+    all_vars.update({"FOURMOMENTUM": F.FOURMOMENTUM})
+    all_vars.update({"ISBASIC": F.ISBASICPARTICLE})
+
+    if basic:
+        all_vars.update({"GHOSTPROB": F.GHOSTPROB})
+        all_vars.update({"ISMUON": F.ISMUON})
+        all_vars.update({"INMUON": F.INMUON})
+        all_vars.update({"INECAL": F.INECAL})
+        all_vars.update({"INHCAL": F.INHCAL})
+        all_vars.update({"HASBREM": F.HASBREM})
+        all_vars.update({"BREMENERGY": F.BREMENERGY})
+        all_vars.update({"BREMBENDCORR": F.BREMBENDCORR})
+        all_vars.update({"BREMPIDE": F.BREMPIDE})
+        all_vars.update({"ECALPIDE": F.ECALPIDE})
+        all_vars.update({"ECALPIDMU": F.ECALPIDMU})
+        all_vars.update({"HCALPIDE": F.HCALPIDE})
+        all_vars.update({"HCALPIDMU": F.HCALPIDMU})
+        all_vars.update({"ELECTRONSHOWEREOP": F.ELECTRONSHOWEREOP})
+        all_vars.update({"CLUSTERMATCH": F.CLUSTERMATCH_CHI2})
+        all_vars.update({"ELECTRONMATCH": F.ELECTRONMATCH_CHI2})
+        all_vars.update({"BREMHYPOMATCH": F.BREMHYPOMATCH_CHI2})
+        all_vars.update({"ELECTRONENERGY": F.ELECTRONENERGY})
+        all_vars.update({"BREMHYPOENERGY": F.BREMHYPOENERGY})
+        all_vars.update({"BREMHYPODELTAX": F.BREMHYPODELTAX})
+        all_vars.update({"ELECTRONID": F.ELECTRONID})
+        all_vars.update({"HCALEOP": F.HCALEOP})
+        # Note: the observables for the two functors below are (TRACK_MOM_X, TRACK_MOM_Y, TRACK_MOM_Z})
+        # and (TRACK_POS_CLOSEST_TO_BEAM_X, TRACK_POS_CLOSEST_TO_BEAM_Y, TRACK_POS_CLOSEST_TO_BEAM_Z),
+        # which is why the trailing underscore in the name is added i.e. "TRACK_MOM_" and "TRACK_POS_CLOSEST_TO_BEAM_"
+        all_vars.update({"TRACK_MOM_": F.TRACK_MOMVEC})
+#        all_vars.update({"TRACK_POS_CLOSESTTOBEAM_": F.TRACK_POSVEC_CLOSESTTOBEAM})
+        
+        all_vars.update({"IS_ABS_ID_pi": F.IS_ABS_ID("pi+")})
+        all_vars.update({"IS_ID_pi": F.IS_ID("pi-")})
+        all_vars.update({"PDG_MASS_pi": F.PDG_MASS("pi+")})
+        all_vars.update({"SIGNED_DELTA_MASS_pi": F.SIGNED_DELTA_MASS("pi+")})
+        all_vars.update({"ABS_DELTA_MASS_pi": F.ABS_DELTA_MASS("pi+")})
+        all_vars.update({"IS_NOT_H": F.IS_NOT_H})
+        all_vars.update({"IS_PHOTON": F.IS_PHOTON})
+
+    all_vars.update({"MASS": F.MASS})
+
+    if comp:
+        all_vars.update({"MAXPT": F.MAX(F.PT)})
+        all_vars.update({"MAXDOCA": F.MAXSDOCA})
+        all_vars.update({"MAXDOCACHI2": F.MAXSDOCACHI2})
+        # the above in cut versions.
+         
+    if comp:
+        all_vars.update({"MINPT": F.MIN(F.PT)})
+    all_vars.update({"MINIP": F.MINIP(pvs)})
+    all_vars.update({"MINIPCHI2": F.MINIPCHI2(pvs)})
+    
+    if basic:
+        all_vars.update({"TRACKPT": F.TRACK_PT})
+        all_vars.update({"TRACKHISTORY": F.VALUE_OR(-1) @ F.TRACKHISTORY @ F.TRACK})
+        all_vars.update({"QOVERP": F.QOVERP @ F.TRACK})
+        all_vars.update({"NDOF": F.VALUE_OR(-1) @ F.NDOF @ F.TRACK})
+        all_vars.update({"NFTHITS": F.VALUE_OR(-1) @ F.NFTHITS @ F.TRACK})
+        all_vars.update({"NHITS": F.VALUE_OR(-1) @ F.NHITS @ F.TRACK})
+        all_vars.update({"NUTHITS": F.VALUE_OR(-1) @ F.NUTHITS @ F.TRACK})
+        all_vars.update({"NVPHITS": F.VALUE_OR(-1) @ F.NVPHITS @ F.TRACK})
+        all_vars.update({"TRACKHASVELO": F.VALUE_OR(-1) @ F.TRACKHASVELO @ F.TRACK})
+        all_vars.update({"TRACKHASUT": F.VALUE_OR(-1) @ F.TRACKHASUT @ F.TRACK})
+         
+    all_vars.update({"OBJECT_KEY": F.OBJECT_KEY})
+
+    all_vars.update({"PHI": F.PHI})
+    
+    all_vars.update({"ABS_PX": F.ABS @ F.PX})
+    
+    all_vars.update({"REFERENCEPOINT_X": F.REFERENCEPOINT_X})
+    all_vars.update({"REFERENCEPOINT_Y": F.REFERENCEPOINT_Y})
+    all_vars.update({"REFERENCEPOINT_Z": F.REFERENCEPOINT_Z})
+
+    if comp:
+        all_vars.update({"SDOCA": F.SDOCA(1, 2)})
+        all_vars.update({"SDOCACHI2": F.SDOCACHI2(1, 2)})
+    if basic:
+        all_vars.update({"SHOWER_SHAPE": F.CALO_NEUTRAL_SHOWER_SHAPE})
+        
+    if comp:
+        all_vars.update({"SUBCOMB12_MM": F.SUBCOMB(Functor=F.MASS, Indices=(1, 2))})
+        all_vars.update({"SUMPT": F.SUM(F.PT)})
+        
+    if basic:
+        all_vars.update({"TX": F.TX})
+        all_vars.update({"TY": F.TY})
+        
+    print(f"### For {ptype} returning variables {all_vars.functor_dict.keys()}")
+    return all_vars
+
+
+def event_variables(PVs, ODIN, decreports, lines):
+    """
+    event variables
+    """
+     
+    evt_vars = FunctorCollection({})
+    evt_vars += FC.EventInfo()
+    
+    evt_vars += FC.SelectionInfo(selection_type="Hlt2", trigger_lines=lines)
+
+    if decreports:                                                                                                                       
+        evt_vars.update(
+            {
+                "DECISIONS": F.DECISIONS(
+                    Lines=[bd2dsk_line + "Decision"], DecReports=decreports
+                )
+            }
+        )
+        evt_vars.update(
+            {
+                "DECREPORTS_FILTER": F.DECREPORTS_FILTER(
+                    Lines=[bd2dsk_line + "Decision"], DecReports=decreports
+                )
+            }
+        )
+         
+    if ODIN:
+        evt_vars.update({"EVENTTYPE": F.EVENTTYPE(ODIN)})
+
+    evt_vars.update({"PV_SIZE": F.SIZE(PVs)})
+    
+    if decreports:
+        evt_vars.update({"TCK": F.TCK(decreports)})
+         
+    print(f"### For event returning variables {evt_vars.functor_dict.keys()}")
+    return evt_vars
+
+
+def template(decay_descriptor, line_name, isturbo, Hlt2_decisions, composite_particles, daughter_particles, file_name=None, ParticleToConstrain=["", ""]):
+
+    evtpath_prefix = "/Event/Spruce/"
+    if isturbo:
+        evtpath_prefix = "/Event/HLT2/"
+
+    input_data_raw = get_particles(evtpath_prefix + f"{line_name}/Particles")
+    input_data = ParticleFilter(Input=input_data_raw, Cut = F.FILTER(F.require_all( F.CHILD(1, F.IS_ABS_ID(ParticleToConstrain[0]))   )))
+    #input_data = ParticleFilter(Input=input_data_raw, Cut = F.FILTER(F.require_all( F.FIND_DECAY( decay_descriptor[composite_particles[0]] ))))
+
+    pvs = get_pvs()
+
+    Hlt1_decisions = [ 
+        'Hlt1TrackMVADecision', 'Hlt1TwoTrackMVADecision', #'Hlt1D2KKDecision',
+        #'Hlt1D2KPiDecision', 'Hlt1D2PiPiDecision',
+        'Hlt1DiMuonHighMassDecision', #'Hlt1DiMuonLowMassDecision',
+        #'Hlt1DiMuonSoftDecision',
+        #'Hlt1KsToPiPiDecision', 'Hlt1LowPtMuonDecision',
+        'Hlt1LowPtDiMuonDecision', 'Hlt1SingleHighPtMuonDecision',
+        'Hlt1TrackMuonMVADecision', "Hlt1DiMuonNoIP_SSDecision", 
+        #"Hlt1DiMuonDrellYan_VLowMassDecision", 
+        #"Hlt1DiMuonDrellYan_VLowMass_SSDecision", 
+        #"Hlt1DiMuonDrellYanDecision", 
+        #"Hlt1DiMuonDrellYan_SSDecision", 
+        #"Hlt1DetJpsiToMuMuPosTagLineDecision", 
+        #"Hlt1DetJpsiToMuMuNegTagLineDecision", 
+        #"Hlt1TrackElectronMVADecision", 
+        #"Hlt1SingleHighPtElectronDecision", 
+        #"Hlt1DiElectronDisplacedDecision", 
+        #"Hlt1SingleHighEtDecision", 
+        #"Hlt1DiPhotonHighMassDecision", 
+        #"Hlt1Pi02GammaGammaDecision", 
+        #"Hlt1DiElectronHighMass_SSDecision", 
+        #"Hlt1DiElectronHighMassDecision",
+        "Hlt1DiMuonNoIPDecision",
+     ]
+
+    composite_variables = FunctorCollection({
+        "END_VX_ERR":F.SQRT @ F.CALL(0,0) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "END_VY_ERR":F.SQRT @ F.CALL(1,1) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "END_VZ_ERR":F.SQRT @ F.CALL(2,2) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "BPVX_ERR": F.SQRT @ F.CALL(0,0) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "BPVY_ERR": F.SQRT @ F.CALL(1,1) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "BPVZ_ERR": F.SQRT @ F.CALL(2,2) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "PERR": F.SQRT @ F.PERR2,
+        "PXERR": F.SQRT @ F.CALL(0,0) @ F.THREE_MOM_COV_MATRIX,
+        "PYERR": F.SQRT @ F.CALL(1,1) @ F.THREE_MOM_COV_MATRIX,
+        "PZERR": F.SQRT @ F.CALL(2,2) @ F.THREE_MOM_COV_MATRIX,
+    })
+
+    composite_variables += HltTisTos( selection_type="Hlt1", trigger_lines=Hlt1_decisions, data=input_data)
+    if not isturbo:
+        composite_variables += HltTisTos( selection_type="Hlt2", trigger_lines=Hlt2_decisions, data=input_data)
+
+    daughter_variables = FunctorCollection({
+        "PERR": F.SQRT @ F.PERR2,
+        "PZERR": F.SQRT @ F.CALL(2,2) @ F.THREE_MOM_COV_MATRIX,
+    })
+
+    #define event level variables
+    odin = get_odin()
+    decreports = None
+    rec_sum=get_rec_summary()
+    event_info = event_variables(pvs, odin, decreports, [line_name]) + FunctorCollection({
+        "nPVs": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nPVs"),
+        "nTTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nTTracks"),
+        "nLongTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nLongTracks"),
+        "nDownstreamTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nDownstreamTracks"),
+        "nUpstreamTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nUpstreamTracks"),
+        "nVeloTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVeloTracks"),
+        "nBackTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nBackTracks"),
+        "nRich1Hits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nRich1Hits"),
+        "nRich2Hits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nRich2Hits"),
+        "nVPClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVPClusters"),
+        "nFTClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nFTClusters"),
+        "eCalTot": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"eCalTot"),
+        "hCalTot": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"hCalTot"),
+        "nEcalClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nEcalClusters"),
+        "ALLPVX": F.ALLPVX(pvs),
+        "ALLPVY": F.ALLPVY(pvs),
+        "ALLPVZ": F.ALLPVZ(pvs),
+    })
+
+    event_info += FC.SelectionInfo(
+        selection_type="Hlt1", trigger_lines=Hlt1_decisions
+    )
+
+    variables = {}
+
+    ParticleToConstrain_PDGName = ParticleToConstrain[0]
+    ParticleToConstrain_Name = ParticleToConstrain[1]
+    print("wmz debug", ParticleToConstrain_PDGName, ParticleToConstrain_Name)
+    
+    for composite_particle in composite_particles:
+        variables[composite_particle] = composite_variables + all_variables(pvs, None, _composite) + make_dtf_variables(pvs, input_data, _composite, ParticleToConstrain_PDGName, ParticleToConstrain_Name)
+    for daughter_particle in daughter_particles:
+        variables[daughter_particle] = daughter_variables + all_variables(pvs, None, _basic) + make_dtf_variables(pvs, input_data, _basic, ParticleToConstrain_PDGName, ParticleToConstrain_Name)
+
+    #define FunTuple instance
+    if not file_name: file_name=line_name
+    my_tuple = Funtuple(
+        name=file_name,
+        tuple_name="DecayTree",
+        fields=decay_descriptor,
+        variables=variables,
+        event_variables=event_info,
+        store_multiple_cand_info = True,
+        inputs=input_data)
+
+    return my_tuple
+
+
+def line_prefilter(line_name):
+    return create_lines_filter(name=f"PreFilter_{line_name}", lines=[line_name, "Hlt2Topo2Body", "Hlt2Topo3Body", "Hlt2_JpsiToMuMuDetachedFull"])
diff --git a/bandq_resprucing/double_charm/dv_data_double_charmonia_OS.py b/bandq_resprucing/double_charm/dv_data_double_charmonia_OS.py
new file mode 100644
index 0000000000..a5e0c04f24
--- /dev/null
+++ b/bandq_resprucing/double_charm/dv_data_double_charmonia_OS.py
@@ -0,0 +1,123 @@
+###############################################################################
+# (c) Copyright 2021-2022 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.                                       #
+###############################################################################
+"""
+Read an HLT2 file and create an ntuple with the new DaVinci configuration.
+"""
+import Functors as F
+import FunTuple.functorcollections as FC
+from FunTuple import FunctorCollection
+from FunTuple import FunTuple_Particles as Funtuple
+from PyConf.reading import get_particles, get_pvs , get_rec_summary
+from DaVinci.algorithms import create_lines_filter
+from DaVinci import Options, make_config
+from DaVinciTools import SubstitutePID
+from FunTuple.functorcollections import SelectionInfo, HltTisTos
+from .tupling import *
+from Hlt2Conf.algorithms_thor import ParticleFilter
+
+def Funtuple_with_filter(
+    line_name,
+    fields,
+    variables,
+    inputs,
+    name="Tuple",
+    tuple_name="DecayTree",
+    event_variables=None,
+):
+    hlt_filter = create_lines_filter(
+        name ="HDRFilter_{hash}",
+        lines=[f"{line_name}"])
+
+    return [
+        hlt_filter,
+        Funtuple(
+            name=name,
+            tuple_name=tuple_name,
+            fields=fields,
+            variables=variables,
+            inputs=inputs,
+            event_variables=event_variables,
+        ),
+    ]
+
+def make_alg_config(Charm1, Charm2, self_conjugate=False, filter=True):
+    line_name = 'SpruceBandQ_DoubleCharmOppositeSign'
+    data_raw = get_particles(f"/Event/Spruce/{line_name}/Particles") 
+    data = ParticleFilter(
+        data_raw,
+        name="data_filtered_{hash}",
+        Cut =F.FILTER(
+            F.require_all( 
+                F.require_any(
+                    F.CHILD(1, F.PARTICLE_ID()==abs(particle_dict[Charm1]["id"])),F.CHILD(1, F.PARTICLE_ID()==-abs(particle_dict[Charm1]["id"]))
+                ),
+                F.require_any(
+                    F.CHILD(2, F.PARTICLE_ID()==abs(particle_dict[Charm2]["id"])),F.CHILD(2, F.PARTICLE_ID()==-abs(particle_dict[Charm2]["id"]))
+                ),
+            ),
+        )
+    )
+    name = "doublecharm_OS_" + Charm1 + Charm2
+    fields = make_fields(Charm1, Charm2, self_conjugate)
+    if filter:
+        return Funtuple_with_filter(
+            line_name,
+            fields,
+            make_var(data, fields,Charm1,Charm2),
+            data,
+            name,
+            event_variables=make_event_info(),
+        )
+    else:
+        return [Funtuple(
+            name=name,
+            tuple_name="DecayTree",
+            fields=fields,
+            variables=make_var(data, fields,Charm1,Charm2),
+            inputs=data,
+            event_variables=make_event_info(),
+        )]
+
+    
+def All():
+    user_algorithms = {}
+    decay_ls = [
+        ["Db0", "D0", True],
+        ["Db0", "Dp"],
+        ["Db0", "Dsp"],
+        ["Db0", "lcp"],
+        ["Db0", "xicz"],
+        ["Db0", "omegacz"],
+        ["Dm", "Dp", True],
+        ["Dm", "Dsp"],
+        ["Dm", "lcp"],
+        ["Dm", "xicz"],
+        ["Dm", "omegacz"],
+        ["Dsm", "Dsp", True],
+        ["Dsm", "lcp"],
+        ["Dsm", "xicz"],
+        ["Dsm", "omegacz"],
+        ["lcm", "lcp", True],
+        ["lcm", "xicz"],
+        ["lcm", "omegacz"],
+        ["xicbz", "xicz", True],
+        ["xicbz", "omegacz"],
+        ["omegacbz", "omegacz", True],
+    ]
+    
+    for decay in decay_ls:
+        if len(decay) == 3:
+            user_algorithms[decay[0]+decay[1]] = make_alg_config(decay[0], decay[1], decay[2])
+        else:
+            user_algorithms[decay[0]+decay[1]] = make_alg_config(decay[0], decay[1])
+    # config = make_config(options, user_algorithms)
+    # return config
+    return user_algorithms
\ No newline at end of file
diff --git a/bandq_resprucing/double_charm/dv_data_double_charmonia_SS.py b/bandq_resprucing/double_charm/dv_data_double_charmonia_SS.py
new file mode 100644
index 0000000000..ba063bd527
--- /dev/null
+++ b/bandq_resprucing/double_charm/dv_data_double_charmonia_SS.py
@@ -0,0 +1,130 @@
+###############################################################################
+# (c) Copyright 2021-2022 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.                                       #
+###############################################################################
+"""
+Read an HLT2 file and create an ntuple with the new DaVinci configuration.
+"""
+import Functors as F
+import FunTuple.functorcollections as FC
+from FunTuple import FunctorCollection
+from FunTuple import FunTuple_Particles as Funtuple
+from PyConf.reading import get_particles, get_pvs , get_rec_summary
+from DaVinci.algorithms import create_lines_filter
+from DaVinci import make_config
+from DaVinciTools import SubstitutePID
+from FunTuple.functorcollections import SelectionInfo, HltTisTos
+from .tupling import *
+from Hlt2Conf.algorithms_thor import ParticleFilter
+
+def Funtuple_with_filter(
+    line_name,
+    fields,
+    variables,
+    inputs,
+    name="Tuple",
+    tuple_name="DecayTree",
+    event_variables=None,
+):
+    hlt_filter = create_lines_filter(
+        name ="HDRFilter_{hash}",
+        lines=[f"{line_name}"])
+
+    return [
+        hlt_filter,
+        Funtuple(
+            name=name,
+            tuple_name=tuple_name,
+            fields=fields,
+            variables=variables,
+            inputs=inputs,
+            event_variables=event_variables,
+        ),
+    ]
+
+def make_alg_config(Charm1, Charm2, self_conjugate=False, filter=True):
+    line_name = 'SpruceBandQ_DoubleCharmSameSign'
+    data_raw = get_particles(f"/Event/Spruce/{line_name}/Particles") 
+    data = ParticleFilter(
+        data_raw,
+        name="data_filtered_{hash}",
+        Cut =F.FILTER(
+            F.require_all( 
+                F.require_any(
+                    F.CHILD(1, F.PARTICLE_ID()==abs(particle_dict[Charm1]["id"])),F.CHILD(1, F.PARTICLE_ID()==-abs(particle_dict[Charm1]["id"]))
+                ),
+                F.require_any(
+                    F.CHILD(2, F.PARTICLE_ID()==abs(particle_dict[Charm2]["id"])),F.CHILD(2, F.PARTICLE_ID()==-abs(particle_dict[Charm2]["id"]))
+                ),
+            ),
+        )
+    )
+    name = "doublecharm_SS_" + Charm1 + Charm2
+    fields = make_fields(Charm1, Charm2)
+    if filter:
+        return Funtuple_with_filter(
+            line_name,
+            fields,
+            make_var(data, fields,Charm1,Charm2),
+            data,
+            name,
+            event_variables=make_event_info(),
+        )
+    else:
+        return [Funtuple(
+            name=name,
+            tuple_name="DecayTree",
+            fields=fields,
+            variables=make_var( data, fields,Charm1,Charm2),
+            inputs=data,
+            event_variables=make_event_info(),
+            )]
+
+    
+def All():
+    user_algorithms = {}
+    decay_ls = [
+        ["D0", "D0"],
+        ["D0", "Dp"],
+        ["D0", "Dsp"],
+        ["D0", "lcp"],
+        ["D0", "xicz"],
+        ["D0", "omegacz"],
+        ["Dp", "Dp"],
+        ["Dp", "Dsp"],
+        ["Dp", "lcp"],
+        ["Dp", "xicz"],
+        ["Dp", "omegacz"],
+        ["Dsp", "Dsp"],
+        ["Dsp", "lcp"],
+        ["Dsp", "xicz"],
+        ["Dsp", "omegacz"],
+        ["lcp", "lcp"],
+        ["lcp", "xicz"],
+        ["lcp", "omegacz"],
+        ["xicz", "xicz"],
+        ["xicz", "omegacz"],
+        ["omegacz", "omegacz"],
+        ["jpsi", "D0"],
+        ["jpsi", "Dp"],
+        ["jpsi", "Dsp"],
+        ["jpsi", "lcp"],
+        ["jpsi", "xicz"],
+        ["jpsi", "omegacz"],
+        ["psi2s", "D0"],
+        ["psi2s", "Dp"],
+        ["psi2s", "Dsp"],
+        ["psi2s", "lcp"],
+        ["psi2s", "xicz"],
+        ["psi2s", "omegacz"],
+    ]
+    
+    for decay in decay_ls:
+        user_algorithms[decay[0]+decay[1]] = make_alg_config(decay[0], decay[1])
+    return user_algorithms
\ No newline at end of file
diff --git a/bandq_resprucing/double_charm/tupling.py b/bandq_resprucing/double_charm/tupling.py
new file mode 100644
index 0000000000..366fc211e1
--- /dev/null
+++ b/bandq_resprucing/double_charm/tupling.py
@@ -0,0 +1,267 @@
+import Functors as F
+import FunTuple.functorcollections as FC
+from FunTuple import FunctorCollection
+from FunTuple import FunTuple_Particles as Funtuple
+from PyConf.reading import get_particles, get_pvs , get_rec_summary
+from DaVinci.algorithms import create_lines_filter
+from DaVinci import make_config
+from FunTuple.functorcollections import SelectionInfo, HltTisTos
+import pandas as pd
+
+particle_dict = {
+    "D0":       {"name":"D0",           "id":421},
+    "Db0":      {"name":"D~0",          "id":-421},
+    "Dp":       {"name":"D+",           "id":411},
+    "Dm":       {"name":"D-",           "id":-411},
+    "Dsp":      {"name":"D_s+",         "id":431},
+    "Dsm":      {"name":"D_s-",         "id":-431},
+    "lcp":      {"name":"Lambda_c+",    "id":4122},
+    "lcm":      {"name":"Lambda_c~-",   "id":-4122},
+    "xicz":     {"name":"Xi_c0",        "id":4132},
+    "xicbz":    {"name":"Xi_c~0",       "id":-4132},
+    "omegacz":  {"name":"Omega_c0",     "id":4332},
+    "omegacbz": {"name":"Omega_c~0",    "id":-4332},
+    "jpsi":     {"name":"J/psi(1S)",    "id":443},
+    "psi2s":    {"name":"psi(2S)",      "id":100443},
+}
+
+Hlt1_decisions = [ 
+    'Hlt1TrackMVADecision', 'Hlt1TwoTrackMVADecision', 'Hlt1D2KKDecision',
+    'Hlt1D2KPiDecision', 'Hlt1D2PiPiDecision',
+    'Hlt1DiMuonHighMassDecision', 'Hlt1DiMuonLowMassDecision',
+    'Hlt1DiMuonSoftDecision',
+    'Hlt1KsToPiPiDecision', 'Hlt1LowPtMuonDecision',
+    'Hlt1LowPtDiMuonDecision', 'Hlt1SingleHighPtMuonDecision',
+    'Hlt1TrackMuonMVADecision'
+]
+
+def make_composite_variables():
+    pvs = get_pvs()
+    variables = FunctorCollection({
+        "ID": F.PARTICLE_ID,
+        "KEY": F.OBJECT_KEY,
+        "PT": F.PT,
+        "PX": F.PX,
+        "PY": F.PY,
+        "PZ": F.PZ,
+        "ENERGY": F.ENERGY,
+        "P": F.P,
+        "M": F.MASS,
+        "ENDVERTEX_CHI2NDOF": F.CHI2DOF,
+        "FOURMOMENTUM": F.FOURMOMENTUM,
+        "BPVDIRA": F.BPVDIRA(pvs),
+        "BPVX": F.BPVX(pvs),
+        "BPVY": F.BPVY(pvs),
+        "BPVZ": F.BPVZ(pvs),
+        "END_VX": F.END_VX,
+        "END_VY": F.END_VY,
+        "END_VZ": F.END_VZ,
+        "END_VZ_ERR":F.SQRT @ F.CALL(2,2) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "BPVZ_ERR": F.SQRT @ F.CALL(2,2) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "TAU": F.BPVLTIME(pvs),
+        "BPVFDCHI2": F.BPVFDCHI2(pvs),
+        "BPVIPCHI2": F.BPVIPCHI2(pvs),
+        "ETA": F.ETA,
+
+    })
+
+    return variables
+
+def make_basic_variables():
+    pvs = get_pvs()
+    variables = FunctorCollection({
+        "ID": F.PARTICLE_ID,
+        "PT": F.PT,
+        "PX": F.PX,
+        "PY": F.PY,
+        "PZ": F.PZ,
+        "M": F.MASS,
+        "ENERGY": F.ENERGY,
+        "P": F.P,
+        "eta": F.ETA,
+        "Track_CHI2NDOF": F.CHI2DOF,
+        "ProbNNK": F.PROBNN_K,
+        "ProbNNpi": F.PROBNN_PI,
+        "PIDK": F.PID_K,
+        "PIDPI": F.PID_PI,
+        "GhostProb": F.GHOSTPROB,
+        "FOURMOMENTUM": F.FOURMOMENTUM,
+        "PERR": F.SQRT @ F.PERR2,
+        "PZERR": F.SQRT @ F.CALL(2,2) @ F.THREE_MOM_COV_MATRIX,
+        "BPVIPCHI2": F.BPVIPCHI2(pvs),
+        "BPVIP": F.BPVIP(pvs),
+    })
+
+    return variables
+
+def make_hlt_variables(data):
+    return HltTisTos( selection_type="Hlt1", trigger_lines=Hlt1_decisions, data=data)    
+
+
+def make_event_info():
+    rec_sum=get_rec_summary()
+    event_info = FC.EventInfo() +FunctorCollection({
+        "nPVs": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nPVs"),
+        "nTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nTracks"),
+        "nTTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nTTracks"),
+        "nLongTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nLongTracks"),
+        "nDownstreamTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nDownstreamTracks"),
+        "nUpstreamTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nUpstreamTracks"),
+        "nVeloTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVeloTracks"),
+        "nBackTracks": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nBackTracks"),
+        "nRich1Hits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nRich1Hits"),
+        "nRich2Hits": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nRich2Hits"),
+        "nVPClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nVPClusters"),
+        "nFTClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nFTClusters"),
+        "eCalTot": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"eCalTot"),
+        "hCalTot": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"hCalTot"),
+        "nEcalClusters": F.VALUE_OR(-1) @F.RECSUMMARY_INFO(rec_sum,"nEcalClusters"),
+    })
+    return event_info
+
+from DecayTreeFitter import DecayTreeFitter
+def make_dtf_variables(data,Charm1,Charm2, is_basic = False, pv_constraint=True):
+    pvs = get_pvs() 
+    if pv_constraint:
+        dtf_name = "DTF_PV_"
+    else:
+        dtf_name = "DTF_"
+    
+    dtf_name = dtf_name + Charm1 + Charm2 + "_"
+    ls_mass_constraints = [particle_dict[Charm1]["name"]]
+    if abs(particle_dict[Charm1]["id"]) != abs(particle_dict[Charm2]["id"]):
+        ls_mass_constraints.append(particle_dict[Charm2]["name"])
+    
+    DTF = DecayTreeFitter(
+        name = dtf_name,
+        input_particles = data,
+        input_pvs = pvs,
+        mass_constraints = ls_mass_constraints
+        #mass_constraints = [particle_dict[Charm1],particle_dict[]],
+        #mass_constraints = ["D~0"],
+    )
+
+    shared_vars = FunctorCollection(
+        {
+            "ETA": F.ETA,
+            "PHI": F.PHI,
+            "BPVIPCHI2": F.BPVIPCHI2(pvs),
+            "BPVIP": F.BPVIP(pvs),
+            "BPVX": F.BPVX(pvs),
+            "BPVY": F.BPVY(pvs),
+            "BPVZ": F.BPVZ(pvs),
+        }
+    ) + FC.Kinematics()
+
+    dtf_quality_vars = FunctorCollection(
+        {
+            dtf_name+"_DTFCHI2": DTF.CHI2,
+            dtf_name+"_DTFNDOF": DTF.NDOF,
+            dtf_name+"_CTAU": DTF.CTAU,
+            dtf_name+"_CTAUERR": DTF.CTAUERR,
+            dtf_name+"_MERR": DTF.MASSERR,
+        }
+    )
+    if is_basic:
+        orig_vars = shared_vars + FunctorCollection(
+            {   
+                "TX": F.TX,
+                "TY": F.TY,
+                "MINIPCHI2": F.MINIPCHI2(pvs),
+                "MINIP": F.MINIP(pvs),
+                "KEY": F.VALUE_OR(-1) @ F.OBJECT_KEY @ F.TRACK,
+                "TRGHOSTPROB": F.GHOSTPROB,
+                "TRACKPT": F.TRACK_PT,
+                "TRACKHISTORY": F.VALUE_OR(-1) @ F.TRACKHISTORY @ F.TRACK,
+                "QOVERP": F.QOVERP @ F.TRACK,
+                "TRCHI2DOF": F.CHI2DOF @ F.TRACK,
+                "NDOF": F.VALUE_OR(-1) @ F.NDOF @ F.TRACK,
+            }
+        )
+    else:
+        orig_vars = shared_vars + FunctorCollection(
+            {   
+                "MAXPT": F.MAX(F.PT),
+                "MINPT": F.MIN(F.PT),
+                "SUMPT": F.SUM(F.PT),
+                "MAXP": F.MAX(F.P),
+                "MINP": F.MIN(F.P),
+                "BPVDIRA": F.BPVDIRA(pvs),
+                "CHI2DOF": F.CHI2DOF, #CHI2VXNDOF
+                "BPVFDCHI2": F.BPVFDCHI2(pvs),
+                "BPVFD": F.BPVFD(pvs),
+                "BPVVDRHO": F.BPVVDRHO(pvs),
+                "BPVVDZ": F.BPVVDZ(pvs),
+                "BPVLTIME": F.BPVLTIME(pvs),
+                "END_VX": F.END_VX, #END_
+                "END_VY": F.END_VY,
+                "END_VZ": F.END_VZ,
+            }
+        )
+        orig_vars += dtf_quality_vars
+    
+    dtf_vars = FunctorCollection({ 
+        dtf_name+expr : DTF(func) for expr,func in orig_vars.get_thor_functors().items() 
+    })
+
+    return dtf_vars
+
+    
+def make_var(data, fields,Charm1,Charm2):
+    var = {}
+    for key in fields.keys():
+        if key == "psi":
+            var[key] = make_composite_variables()+make_hlt_variables(data)+make_dtf_variables(data,Charm1,Charm2,False,True),
+            var[key] = var[key][0]
+        elif key.startswith("Charm"): 
+            var[key] = make_composite_variables() + make_dtf_variables(data, Charm1,Charm2,False,True)
+        else: 
+            var[key] = make_basic_variables() + make_dtf_variables(data, Charm1,Charm2,True,True)
+    return var
+
+def make_fields(Charm1, Charm2, self_conjugate=False):
+    dec_dict = {
+        "D0": "(D0 -> K- pi+)",
+        "Db0": "(D~0 -> K+ pi-)",
+        "Dp": "(D+ -> K- pi+ pi+)",
+        "Dm": "(D- -> K+ pi- pi-)",
+        "Dsp": "(D_s+ -> K- K+ pi+)",
+        "Dsm": "(D_s- -> K+ K- pi-)",
+        "lcp": "(Lambda_c+ -> p+ K- pi+)",
+        "lcm": "(Lambda_c~- -> p~- K+ pi-)",
+        "xicz": "(Xi_c0 -> p+ K- K- pi+)",
+        "xicbz": "(Xi_c~0 -> p~- K+ K+ pi-)",
+        "omegacz": "(Omega_c0 -> p+ K- K- pi+)",
+        "omegacbz": "(Omega_c~0 -> p~- K+ K+ pi-)",
+        "jpsi": "(J/psi(1S) -> mu- mu+)",
+        "psi2s": "(psi(2S) -> mu- mu+)",
+    }
+    # Ensure Charm1 and Charm2 are valid keys in dec_dict
+    if Charm1 not in dec_dict or Charm2 not in dec_dict:
+        raise ValueError("Invalid Charm1 or Charm2 provided.")
+    fields = {}
+
+    # Creat the decay descriptor for Charms.
+    fields["psi"] = "[psi(3770) -> " + dec_dict[Charm1] + " " + dec_dict[Charm2] + "]CC"
+    fields["Charm1"] = "[psi(3770) -> ^" + dec_dict[Charm1] + " " + dec_dict[Charm2] + "]CC"
+    fields["Charm2"] = "[psi(3770) -> " + dec_dict[Charm1] + " ^" + dec_dict[Charm2] + "]CC"
+
+    # Creat the decay descriptor for final kaons and pions.
+    Charm1_split = dec_dict[Charm1].split(" ")
+    N_final_state1 = len(Charm1_split) - 2
+    Charm2_split = dec_dict[Charm2].split(" ")
+    N_final_state2 = len(Charm2_split) - 2
+    for i in range(N_final_state1):
+        string = Charm1_split[:2+i] + ['^'] + Charm1_split[2+i:] 
+        subdecay = ' '.join(string)
+        fields["h1"+str(i+1)] = "[psi(3770) -> " + subdecay.replace("^ ", "^") + " " + dec_dict[Charm2] + "]CC"
+    for i in range(N_final_state2):   
+        string = Charm2_split[:2+i] + ['^'] + Charm2_split[2+i:] 
+        subdecay = ' '.join(string)
+        fields["h2"+str(i+1)] = "[psi(3770) -> " + dec_dict[Charm1]  + " " + subdecay.replace("^ ", "^") + "]CC"
+    
+    # Removing 'CC' to avoid double counting. Seems not working.
+    if self_conjugate:
+        for key, values in fields.items():
+            fields[key] = values[1:-3]
+    return fields
\ No newline at end of file
diff --git a/bandq_resprucing/info.yaml b/bandq_resprucing/info.yaml
new file mode 100644
index 0000000000..817bc80cb6
--- /dev/null
+++ b/bandq_resprucing/info.yaml
@@ -0,0 +1,35 @@
+defaults:
+  application: "DaVinci/v64r13@x86_64_v2-el9-clang16-opt"
+  output: DATA.ROOT
+  inform:
+    - yajing.wei@cern.ch
+    - mengzhen.wang@cern.ch
+  wg: BandQ
+
+{%- set datasets = [
+  ('Sprucing24r1', '-Excl-UT', 'Down'),
+  ('Sprucing24r1', '-Excl-UT', 'Up'),
+  ('Sprucing24r1', '', 'Down'),
+  ('Sprucing24r1', '', 'Up'),
+]%}
+{%- for version, condition, polarity in datasets %}
+Bu2D0H_D02KsHH_{{ version }}_{{ polarity }}{{ condition }}:
+  options:
+    entrypoint: bandq_resprucing.tupling:entry_point
+    extra_options:
+      input_raw_format: 0.5
+      input_type: ROOT # ROOT for SprucingPass, RAW for RAW data (Hlt2 output)
+      simulation: False
+      data_type: "Upgrade"
+      geometry_version: run3/2024.Q1.2-v00.00
+      conditions_version: master
+      input_process: "Spruce" # for SprucingPass, "Hlt2" for RAW data (Hlt2 output)
+      input_stream: "bandq" # for streamed data
+  input:
+    # bk_query: "/validation/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}{{ condition }}/Real Data/{{ version }}/90000000/BANDQ.DST"
+    bk_query: "/LHCb/Collision24/Beam6800GeV-VeloClosed-MagUp/Real Data/Sprucing24c4/90000000/BANDQ.DST"
+    dq_flags:
+      - UNCHECKED
+      - OK
+    n_test_lfns: 1 
+{%- endfor %}
diff --git a/bandq_resprucing/tupling.py b/bandq_resprucing/tupling.py
new file mode 100644
index 0000000000..0071b68347
--- /dev/null
+++ b/bandq_resprucing/tupling.py
@@ -0,0 +1,33 @@
+from .B2psiX.dv_data import *
+from .BforSpectroscopy.dv_data import *
+from .double_charm.dv_data_double_charmonia_OS import All as double_charmonia_OS
+from .double_charm.dv_data_double_charmonia_SS import All as double_charmonia_SS 
+
+def entry_point(options: Options):
+    algs_merged = Lb2JpsipK()
+    algs_merged.update(Lb2Jpsippi())
+    algs_merged.update(B2JpsiKpi())
+    algs_merged.update(Bp2JpsiphiK())
+    algs_merged.update(B2Jpsippbar())
+    algs_merged.update(Xib2JpsiLambdaK_LL())
+    algs_merged.update(Xib2JpsiLambdaK_DD())
+    algs_merged.update(Bp2JpsipLambdabar_LL())
+    algs_merged.update(Bp2JpsipLambdabar_DD())
+    algs_merged.update(Bp2JpsiKS0pi_LL())
+    algs_merged.update(Bp2JpsiKS0pi_DD())
+    
+    algs_merged.update(Bu())
+    algs_merged.update(Bd())
+    algs_merged.update(Bs())
+    algs_merged.update(Bc())
+    algs_merged.update(Lb())
+    algs_merged.update(Xibm())
+    algs_merged.update(Xib0())
+    algs_merged.update(Omegab())
+    algs_merged.update(SL())
+
+
+    algs_merged.update(double_charmonia_OS())
+    algs_merged.update(double_charmonia_SS())
+
+    return make_config(options, algs_merged)
\ No newline at end of file
-- 
GitLab


From a0bc0d817292dc58c89de6edfc829d2b337807d8 Mon Sep 17 00:00:00 2001
From: yajing <yajing.wei@cern.ch>
Date: Wed, 27 Nov 2024 14:46:33 +0000
Subject: [PATCH 2/6] Change bkk path to validation sample

---
 bandq_resprucing/info.yaml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/bandq_resprucing/info.yaml b/bandq_resprucing/info.yaml
index 817bc80cb6..c1fd8d92c0 100644
--- a/bandq_resprucing/info.yaml
+++ b/bandq_resprucing/info.yaml
@@ -26,8 +26,7 @@ Bu2D0H_D02KsHH_{{ version }}_{{ polarity }}{{ condition }}:
       input_process: "Spruce" # for SprucingPass, "Hlt2" for RAW data (Hlt2 output)
       input_stream: "bandq" # for streamed data
   input:
-    # bk_query: "/validation/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}{{ condition }}/Real Data/{{ version }}/90000000/BANDQ.DST"
-    bk_query: "/LHCb/Collision24/Beam6800GeV-VeloClosed-MagUp/Real Data/Sprucing24c4/90000000/BANDQ.DST"
+    bk_query: "/validation/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}{{ condition }}/Real Data/{{ version }}/90000000/BANDQ.DST"
     dq_flags:
       - UNCHECKED
       - OK
-- 
GitLab


From 8d048a73d3252a344b44da300fd02d4bef9fd3c9 Mon Sep 17 00:00:00 2001
From: yajing <yajing.wei@cern.ch>
Date: Thu, 5 Dec 2024 17:27:30 +0000
Subject: [PATCH 3/6] Change number of lfn

---
 bandq_resprucing/info.yaml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/bandq_resprucing/info.yaml b/bandq_resprucing/info.yaml
index c1fd8d92c0..1ac4b80c4c 100644
--- a/bandq_resprucing/info.yaml
+++ b/bandq_resprucing/info.yaml
@@ -13,7 +13,7 @@ defaults:
   ('Sprucing24r1', '', 'Up'),
 ]%}
 {%- for version, condition, polarity in datasets %}
-Bu2D0H_D02KsHH_{{ version }}_{{ polarity }}{{ condition }}:
+BandQ_Resprucing_{{ version }}_{{ polarity }}{{ condition }}:
   options:
     entrypoint: bandq_resprucing.tupling:entry_point
     extra_options:
@@ -25,6 +25,7 @@ Bu2D0H_D02KsHH_{{ version }}_{{ polarity }}{{ condition }}:
       conditions_version: master
       input_process: "Spruce" # for SprucingPass, "Hlt2" for RAW data (Hlt2 output)
       input_stream: "bandq" # for streamed data
+      n_test_lfns: 5
   input:
     bk_query: "/validation/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}{{ condition }}/Real Data/{{ version }}/90000000/BANDQ.DST"
     dq_flags:
-- 
GitLab


From 4b124faeffca0c69ca55a6ceae86eb3edddea47f Mon Sep 17 00:00:00 2001
From: yajing <yajing.wei@cern.ch>
Date: Thu, 5 Dec 2024 21:06:18 +0000
Subject: [PATCH 4/6] Fix number of lfn

---
 bandq_resprucing/info.yaml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/bandq_resprucing/info.yaml b/bandq_resprucing/info.yaml
index 1ac4b80c4c..a5cb83d6e6 100644
--- a/bandq_resprucing/info.yaml
+++ b/bandq_resprucing/info.yaml
@@ -25,11 +25,10 @@ BandQ_Resprucing_{{ version }}_{{ polarity }}{{ condition }}:
       conditions_version: master
       input_process: "Spruce" # for SprucingPass, "Hlt2" for RAW data (Hlt2 output)
       input_stream: "bandq" # for streamed data
-      n_test_lfns: 5
   input:
     bk_query: "/validation/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}{{ condition }}/Real Data/{{ version }}/90000000/BANDQ.DST"
     dq_flags:
       - UNCHECKED
       - OK
-    n_test_lfns: 1 
+    n_test_lfns: 5 
 {%- endfor %}
-- 
GitLab


From 3a790ff482993e6a79c45745d7af26805555cb2a Mon Sep 17 00:00:00 2001
From: yajing <yajing.wei@cern.ch>
Date: Fri, 6 Dec 2024 01:59:06 +0000
Subject: [PATCH 5/6] Change number of lfn

---
 bandq_resprucing/info.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bandq_resprucing/info.yaml b/bandq_resprucing/info.yaml
index a5cb83d6e6..1f89ab0057 100644
--- a/bandq_resprucing/info.yaml
+++ b/bandq_resprucing/info.yaml
@@ -30,5 +30,5 @@ BandQ_Resprucing_{{ version }}_{{ polarity }}{{ condition }}:
     dq_flags:
       - UNCHECKED
       - OK
-    n_test_lfns: 5 
+    n_test_lfns: 3 
 {%- endfor %}
-- 
GitLab


From 21d2b1cc010711fe7e5a053c6e7c2261f22b120f Mon Sep 17 00:00:00 2001
From: yajing <yajing.wei@cern.ch>
Date: Fri, 6 Dec 2024 09:33:46 +0000
Subject: [PATCH 6/6] Modify tuple names

---
 bandq_resprucing/B2psiX/dv_data.py           | 22 ++---
 bandq_resprucing/BforSpectroscopy/dv_data.py | 84 ++++++++++----------
 2 files changed, 53 insertions(+), 53 deletions(-)

diff --git a/bandq_resprucing/B2psiX/dv_data.py b/bandq_resprucing/B2psiX/dv_data.py
index 38bf0f5177..1404f2753e 100644
--- a/bandq_resprucing/B2psiX/dv_data.py
+++ b/bandq_resprucing/B2psiX/dv_data.py
@@ -1,52 +1,52 @@
 from .template import *
 
 def Lb2JpsipK():
-    algs = template(["Lambda_b0", "J/psi(1S)", "p+", "K-"], True, "Lb2JpsipK")
+    algs = template(["Lambda_b0", "J/psi(1S)", "p+", "K-"], True, "B2psiX_Lb2JpsipK")
     #  return make_config( options, algs )
     return algs
 
 def Lb2Jpsippi():
-    algs = template(["Lambda_b0", "J/psi(1S)", "p+", "pi-"], True, "Lb2Jpsippi")
+    algs = template(["Lambda_b0", "J/psi(1S)", "p+", "pi-"], True, "B2psiX_Lb2Jpsippi")
     #  return make_config( options, algs )
     return algs
 
 def B2JpsiKpi():
-    algs = template(["B0", "J/psi(1S)", "K+", "pi-"], True, "B2JpsiKpi")
+    algs = template(["B0", "J/psi(1S)", "K+", "pi-"], True, "B2psiX_B2JpsiKpi")
     #  return make_config( options, algs )
     return algs
 
 def Bp2JpsiphiK():
-    algs = template(["B+", "J/psi(1S)", "phi(1020)", "K+"], True, "Bp2JpsiphiK")
+    algs = template(["B+", "J/psi(1S)", "phi(1020)", "K+"], True, "B2psiX_Bp2JpsiphiK")
 #      return make_config( options, algs )
     return algs
 
 def B2Jpsippbar():
-    algs = template(["B_s0", "J/psi(1S)", "p+", "p~-"], False, "B2Jpsippbar")
+    algs = template(["B_s0", "J/psi(1S)", "p+", "p~-"], False, "B2psiX_B2Jpsippbar")
 #      return make_config( options, algs )
     return algs
 
 def Xib2JpsiLambdaK_LL():
-    algs = template(["Xi_b-", "J/psi(1S)", "Lambda0", "K-"], True, "Xib2JpsiLambdaK_LL") #LL 
+    algs = template(["Xi_b-", "J/psi(1S)", "Lambda0", "K-"], True, "B2psiX_Xib2JpsiLambdaK_LL") #LL 
     return algs
 
 def Xib2JpsiLambdaK_DD():
-    algs = template(["Xi_b-", "J/psi(1S)", "Lambda0_DD", "K-"], True, "Xib2JpsiLambdaK_DD") #DD
+    algs = template(["Xi_b-", "J/psi(1S)", "Lambda0_DD", "K-"], True, "B2psiX_Xib2JpsiLambdaK_DD") #DD
     return algs
 
 def Bp2JpsipLambdabar_LL():
-    algs = template(["B+", "J/psi(1S)", "p+", "Lambda~0"], True, "Bp2JpsipLambdabar_LL")
+    algs = template(["B+", "J/psi(1S)", "p+", "Lambda~0"], True, "B2psiX_Bp2JpsipLambdabar_LL")
     return algs
 
 def Bp2JpsipLambdabar_DD():
-    algs = template(["B+", "J/psi(1S)", "p+", "Lambda~0_DD"], True, "Bp2JpsipLambdabar_DD")
+    algs = template(["B+", "J/psi(1S)", "p+", "Lambda~0_DD"], True, "B2psiX_Bp2JpsipLambdabar_DD")
     return algs
 
 def Bp2JpsiKS0pi_LL():
-    algs = template(["B+", "J/psi(1S)", "KS0", "pi+"], True, "Bp2JpsiKS0pi_LL")
+    algs = template(["B+", "J/psi(1S)", "KS0", "pi+"], True, "B2psiX_Bp2JpsiKS0pi_LL")
     return algs
 
 def Bp2JpsiKS0pi_DD():
-    algs = template(["B+", "J/psi(1S)", "KS0_DD", "pi+"], True, "Bp2JpsiKS0pi_DD")
+    algs = template(["B+", "J/psi(1S)", "KS0_DD", "pi+"], True, "B2psiX_Bp2JpsiKS0pi_DD")
     return algs
 
 # def entry_point(options: Options):
diff --git a/bandq_resprucing/BforSpectroscopy/dv_data.py b/bandq_resprucing/BforSpectroscopy/dv_data.py
index 6dd5300fc3..554ffb9c8c 100644
--- a/bandq_resprucing/BforSpectroscopy/dv_data.py
+++ b/bandq_resprucing/BforSpectroscopy/dv_data.py
@@ -32,7 +32,7 @@ def Bu():
             },
             ["Bu", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Bu_K1"],
-            "BuToJpsiK",
+            "BforSpectroscopy_BuToJpsiK",
             ["J/psi(1S)", "Jpsi"],
         ),
         
@@ -47,7 +47,7 @@ def Bu():
             },
             ["Bu", "D0"],
             ["D0_K1", "D0_Pi1", "Bu_Pi1"],
-            "BuToD0Pi",
+            "BforSpectroscopy_BuToD0Pi",
             ["D~0", "D0"],
         ),
 
@@ -64,7 +64,7 @@ def Bu():
             },
             ["Bu", "D0"],
             ["D0_K1", "D0_Pi1", "Bu_Pi1", "Bu_Pi2", "Bu_Pi3"],
-            "BuToD0PiPiPi",
+            "BforSpectroscopy_BuToD0PiPiPi",
             ["D~0", "D0"],
         ),
 
@@ -81,7 +81,7 @@ def Bu():
             },
             ["Bu", "Dm"],
             ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bu_Pi1", "Bu_Pi2"],
-            "BuToDmPipPip",
+            "BforSpectroscopy_BuToDmPipPip",
             ["D-", "Dm"],
         ),
 
@@ -99,7 +99,7 @@ def Bu():
             },
             ["Bu", "D0"],
             ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bu_Pi1"],
-            "Bu2D0Pi_K3Pi",
+            "BforSpectroscopy_Bu2D0Pi_K3Pi",
             ["D~0", "D0"],
         ),
 
@@ -118,7 +118,7 @@ def Bu():
             },
             ["Bu", "D0"],
             ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bu_Pi1", "Bu_Pi2", "Bu_Pi3"],
-            "Bu2D0PiPiPi_K3Pi",
+            "BforSpectroscopy_Bu2D0PiPiPi_K3Pi",
             ["D~0", "D0"],
         )
     ]
@@ -157,7 +157,7 @@ def Bd():
             },
             ["Bd", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Bd_K1", "Bd_Pi1"],
-            "BdToJpsiKPi",
+            "BforSpectroscopy_BdToJpsiKPi",
             ["J/psi(1S)", "Jpsi"],
         ),
 
@@ -173,7 +173,7 @@ def Bd():
             },
             ["Bd", "Dm"],
             ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bd_Pi1"],
-            "BdToDmPip",
+            "BforSpectroscopy_BdToDmPip",
             ["D-", "Dm"],
         ),
 
@@ -189,7 +189,7 @@ def Bd():
             },
             ["Bd", "D0"],
             ["D0_K1", "D0_Pi1", "Bd_Pi1", "Bd_Pi2"],
-            "BdToD0barPiPi",
+            "BforSpectroscopy_BdToD0barPiPi",
             ["D~0", "D0"],
         ),
 
@@ -207,7 +207,7 @@ def Bd():
             },
             ["Bd", "Dm"],
             ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bd_Pi1", "Bd_Pi2", "Bd_Pi3"],
-            "BdToDmPipPipPim",
+            "BforSpectroscopy_BdToDmPipPipPim",
             ["D-", "Dm"],
         ),
         # Bd -> D0bar pi+ pi-, with D0bar -> K+ pi- pi+ pi-
@@ -224,7 +224,7 @@ def Bd():
             },
             ["Bd", "D0"],
             ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bd_Pi1", "Bd_Pi2"],
-            "BdToD0barPiPi_K3Pi",
+            "BforSpectroscopy_BdToD0barPiPi_K3Pi",
             ["D~0", "D0"],
         )
     ]
@@ -265,7 +265,7 @@ def Bs():
             },
             ["Bs", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Bs_K1", "Bs_K2"],
-            "BsToJpsiKK",
+            "BforSpectroscopy_BsToJpsiKK",
             ["J/psi(1S)", "Jpsi"],
         ),
         
@@ -281,7 +281,7 @@ def Bs():
             },
             ["Bs", "Ds"],
             ["Ds_K1", "Ds_K2", "Ds_Pi1", "Bs_Pi1"],
-            "BsToDsmPip",
+            "BforSpectroscopy_BsToDsmPip",
             ["D_s-", "Ds"],
         ),
 
@@ -299,7 +299,7 @@ def Bs():
             },
             ["Bs", "Ds"],
             ["Ds_K1", "Ds_K2", "Ds_Pi1", "Bs_Pi1", "Bs_Pi2", "Bs_Pi3"],
-            "BsToDsmPipPipPim",
+            "BforSpectroscopy_BsToDsmPipPipPim",
             ["D_s-", "Ds"],
         ),
         
@@ -317,7 +317,7 @@ def Bs():
             },
             ["Bs", "D0"],
             ["D0_K1", "D0_Pi1", "D0_Pi2", "D0_Pi3", "Bs_K1", "Bs_Pi1"],
-            "BsToD0bKmPip_K3Pi",
+            "BforSpectroscopy_BsToD0bKmPip_K3Pi",
             ["D~0", "D0"],
         ),
 
@@ -333,7 +333,7 @@ def Bs():
             },
             ["Bs", "D0"],
             ["D0_K1", "D0_Pi1", "Bs_K1", "Bs_Pi1"],
-            "BsToD0bKmPip",
+            "BforSpectroscopy_BsToD0bKmPip",
             ["D~0", "D0"],
         )
     ]
@@ -370,7 +370,7 @@ def Bc():
             },
             ["Bc", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Bc_Pi1"],
-            "BcToJpsiPi",
+            "BforSpectroscopy_BcToJpsiPi",
             ["J/psi(1S)", "Jpsi"],
         ),
 
@@ -387,7 +387,7 @@ def Bc():
             },
             ["Bc", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Bc_Pi1", "Bc_Pi2", "Bc_Pi3"],
-            "BcToJpsiPiPiPi",
+            "BforSpectroscopy_BcToJpsiPiPiPi",
             ["J/psi(1S)", "Jpsi"],
         )
     ]
@@ -425,7 +425,7 @@ def Lb():
             },
             ["Lb", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Lb_P1", "Lb_K1"],
-            "LbToJpsiPK",
+            "BforSpectroscopy_LbToJpsiPK",
             ["J/psi(1S)", "Jpsi"],
         ),
 
@@ -441,7 +441,7 @@ def Lb():
             },
             ["Lb", "Lc"],
             ["Lc_P1", "Lc_K1", "Lc_Pi1", "Lb_Pi1"],
-            "LbToLcPim",
+            "BforSpectroscopy_LbToLcPim",
             ["Lambda_c+", "Lc"],
         ),
 
@@ -459,7 +459,7 @@ def Lb():
             },
             ["Lb", "Lc"],
             ["Lc_P1", "Lc_K1", "Lc_Pi1", "Lb_Pi1", "Lb_Pi2", "Lb_Pi3"],
-            "LbToLcPipPimPim",
+            "BforSpectroscopy_LbToLcPipPimPim",
             ["Lambda_c+", "Lc"],
         )
     ]
@@ -502,7 +502,7 @@ def Xibm():
              },
              ["Xibm", "Jpsi", "Xi", "Lambda"],
              ["Jpsi_mup", "Jpsi_mum", "Lambda_P1", "Lambda_Pi1", "Xi_Pi1"],
-             "XibmToJpsiXi",
+             "BforSpectroscopy_XibmToJpsiXi",
             ["J/psi(1S)", "Jpsi"],
          ),
 
@@ -519,7 +519,7 @@ def Xibm():
             },
             ["Xibm", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Xibm_P1", "Xibm_K1", "Xibm_K2"],
-            "XibmToJpsiPKK",
+            "BforSpectroscopy_XibmToJpsiPKK",
             ["J/psi(1S)", "Jpsi"],
         ),
 
@@ -536,7 +536,7 @@ def Xibm():
             },
             ["Xibm", "Xic0"],
             ["Xic0_P1", "Xic0_K1", "Xic0_K2", "Xic0_Pi1", "Xibm_Pi1"],
-            "XibmToXic0Pim",
+            "BforSpectroscopy_XibmToXic0Pim",
             ["Xi_c0", "Xic0"],
         ),
 
@@ -553,7 +553,7 @@ def Xibm():
             },
             ["Xibm", "Lc"],
             ["Lc_P1", "Lc_K1", "Lc_Pi1", "Xibm_K1", "Xibm_Pi1"],
-            "XibmToLcKmPim",
+            "BforSpectroscopy_XibmToLcKmPim",
             ["Lambda_c+", "Lc"],
         )
     ]
@@ -598,7 +598,7 @@ def Xib0():
              },
              ["Xib0", "Jpsi", "Xi", "Lambda"],
              ["Jpsi_mup", "Jpsi_mum", "Lambda_P1", "Lambda_Pi1", "Xi_Pi1", "Xib0_Pi1"],
-             "Xib0ToJpsiXiPi", 
+             "BforSpectroscopy_Xib0ToJpsiXiPi", 
             ["J/psi(1S)", "Jpsi"],
          ),
 
@@ -616,7 +616,7 @@ def Xib0():
             },
             ["Xib0", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Xib0_P1", "Xib0_K1", "Xib0_K2", "Xib0_Pi1"],
-            "Xib0ToJpsiPKKPi", 
+            "BforSpectroscopy_Xib0ToJpsiPKKPi", 
             ["J/psi(1S)", "Jpsi"],
         ),
 
@@ -632,7 +632,7 @@ def Xib0():
             },
             ["Xib0", "Xicp"],
             ["Xicp_P1", "Xicp_K1", "Xicp_Pi1", "Xib0_Pi1"],
-            "Xib0ToXicpPim",
+            "BforSpectroscopy_Xib0ToXicpPim",
             ["Xi_c+", "Xicp"],
         ),
 
@@ -650,7 +650,7 @@ def Xib0():
             },
             ["Xib0", "Lc"],
             ["Lc_P1", "Lc_K1", "Lc_Pi1", "Xib0_K1", "Xib0_Pi1", "Xib0_Pi2"],
-            "Xib0ToLcKmPipPim",
+            "BforSpectroscopy_Xib0ToLcKmPipPim",
             ["Lambda_c+", "Lc"],
         )
     ]
@@ -694,7 +694,7 @@ def Omegab():
              },
              ["Omegab", "Jpsi", "Omega", "Lambda"],
              ["Jpsi_mup", "Jpsi_mum", "Lambda_P1", "Lambda_Pi1", "Omega_K1"],
-             "OmegabToJpsiOmega", 
+             "BforSpectroscopy_OmegabToJpsiOmega", 
             ["J/psi(1S)", "Jpsi"],
          ),
 
@@ -713,7 +713,7 @@ def Omegab():
             },
             ["Omegab", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Omegab_P1", "Omegab_K1", "Omegab_K2", "Omegab_K3", "Omegab_Pi1"],
-            "OmegabToJpsiPKKKPi",
+            "BforSpectroscopy_OmegabToJpsiPKKKPi",
             ["J/psi(1S)", "Jpsi"],
         ),
 
@@ -730,7 +730,7 @@ def Omegab():
             },
             ["Omegab", "Omegac"],
             ["Omegac_P1", "Omegac_K1", "Omegac_K2", "Omegac_Pi1", "Omegab_Pi1"],
-            "OmegabToOmcPim",
+            "BforSpectroscopy_OmegabToOmcPim",
             ["Omega_c0", "Omegac"],
         ),
 
@@ -747,7 +747,7 @@ def Omegab():
             },
             ["Omegab", "Xic"],
             ["Xic_P1", "Xic_K1", "Xic_Pi1", "Omegab_K1", "Omegab_Pi1"],
-            "OmegabToXicpKmPim",
+            "BforSpectroscopy_OmegabToXicpKmPim",
             ["Xi_c+", "Xic"],
         ),
 
@@ -766,7 +766,7 @@ def Omegab():
             },
             ["Omegab", "Lc"],
             ["Lc_P1", "Lc_K1", "Lc_Pi1", "Omegab_K1", "Omegab_K2", "Omegab_Pi1", "Omegab_Pi2"],
-            "OmegabToLcpKmKmPipPim",
+            "BforSpectroscopy_OmegabToLcpKmKmPipPim",
             ["Lambda_c+", "Lc"],
         )
     ]
@@ -800,7 +800,7 @@ def SL():
             },
             ["Bu", "D0"],
             ["D0_K1", "D0_Pi1", "D0_mu1"],
-            "BuToD0mu",
+            "BforSpectroscopy_BuToD0mu",
             ["D~0", "D0"],
         ),
         # Bd -> D+ mu- decay, with D+ -> K- pi+ pi+
@@ -815,7 +815,7 @@ def SL():
             },
             ["Bd", "Dm"],
             ["Dm_K1", "Dm_Pi1", "Dm_Pi2", "Bd_mu1"],
-            "BdToDmmu",
+            "BforSpectroscopy_BdToDmmu",
             ["D-", "Dm"],
         ),
         # Bs -> D_s- mu+ decay (corrected)
@@ -830,7 +830,7 @@ def SL():
             },
             ["Bs", "Ds"],
             ["Ds_K1", "Ds_K2", "Ds_Pi1", "Bs_mu1"],
-            "BsToDsmmu",
+            "BforSpectroscopy_BsToDsmmu",
             ["D_s-", "Ds"],
         ),
         # Bc -> J/psi mu+ decay
@@ -844,7 +844,7 @@ def SL():
             },
             ["Bc", "Jpsi"],
             ["Jpsi_mup", "Jpsi_mum", "Bc_mu1"],
-            "BcToJpsimu", 
+            "BforSpectroscopy_BcToJpsimu", 
             ["J/psi(1S)", "Jpsi"],
         ),
         # Lb -> Lc+ mu- decay, with Lc+ -> p+ K- pi+
@@ -859,7 +859,7 @@ def SL():
             },
             ["Lb", "Lc"],
             ["Lc_P1", "Lc_K1", "Lc_Pi1", "Lb_mu1"],
-            "LbToLcmu",
+            "BforSpectroscopy_LbToLcmu",
             ["Lambda_c+", "Lc"],
         ),
         # Xibm -> Xic0 mu-, with Xic0 -> p+ K- K- pi+
@@ -875,7 +875,7 @@ def SL():
             },
             ["Xibm", "Xic0"],
             ["Xic0_P1", "Xic0_K1", "Xic0_K2", "Xic0_Pi1", "Xibm_mu1"],
-            "XibmToXic0mu",
+            "BforSpectroscopy_XibmToXic0mu",
             ["Xi_c0", "Xic0"],
         ),
         # Xib0 -> Xic+ mu-, with Xic+ -> p+ K- pi+
@@ -890,7 +890,7 @@ def SL():
             },
             ["Xib0", "Xicp"],
             ["Xicp_P1", "Xicp_K1", "Xicp_Pi1", "Xib0_mu1"],
-            "Xib0ToXicpmu",
+            "BforSpectroscopy_Xib0ToXicpmu",
             ["Xi_c+", "Xicp"],
         ),
         # Omb -> Omega_c0 mu-, with Omega_c0 -> p+ K- K- pi+
@@ -906,7 +906,7 @@ def SL():
             },
             ["Omegab", "Omegac"],
             ["Omegac_P1", "Omegac_K1", "Omegac_K2", "Omegac_Pi1", "Omegab_mu1"],
-            "OmegabToOmcmu",
+            "BforSpectroscopy_OmegabToOmcmu",
             ["Omega_c0", "Omegac"],
         ),
     ]
-- 
GitLab