diff --git a/QuarkoniaToMuMu2024/dv_mc_Jpsi2mumu.py b/QuarkoniaToMuMu2024/dv_mc_Jpsi2mumu.py
new file mode 100644
index 0000000000000000000000000000000000000000..7de5d8d9872132a7a25b39c66b9c36a48210788d
--- /dev/null
+++ b/QuarkoniaToMuMu2024/dv_mc_Jpsi2mumu.py
@@ -0,0 +1,36 @@
+import os
+import sys
+sys.path.append(os.path.join(
+    os.environ['ANALYSIS_PRODUCTIONS_BASE'], 'QuarkoniaToMuMu2024'))
+from helpers import dv_tree_nopid, dv_tree, dv_mctree, private_combiners
+from DaVinci import Options, make_config
+from RecoConf.event_filters import require_pvs
+
+from RecoConf.reconstruction_objects import make_pvs
+
+
+
+def main(options: Options):
+    decay_descriptor = {
+        "QQbar": "J/psi(1S) -> mu+ mu-",
+        "mup": "J/psi(1S) -> ^mu+ mu-",
+        "mum": "J/psi(1S) -> mu+ ^mu-",
+    }
+    decay_descriptor_mctree = {
+        "QQbar": "J/psi(1S) ==> mu+ mu-",
+        "mup": "J/psi(1S) ==> ^mu+ mu-",
+        "mum": "J/psi(1S) ==> mu+ ^mu-",
+    }
+
+    line_name = 'Hlt2_JpsiToMuMu'
+    my_filter = dv_tree.line_prefilter(line_name)
+    my_tuple_nopid = dv_tree_nopid.tree_template(decay_descriptor, line_name, True, [], private_combiners.make_jpsi())
+    my_tuple = dv_tree.tree_template(decay_descriptor, line_name, True, [])
+    my_mctuple = dv_mctree.mctree_template(decay_descriptor_mctree)
+    user_algorithms = {
+            'Alg_tuple_nopid': [ require_pvs(make_pvs()), my_tuple_nopid],
+            'Alg_tuple': [my_filter, my_tuple],
+            'Alg_mctuple': [my_mctuple],
+            }
+    return make_config(options, user_algorithms) 
+
diff --git a/QuarkoniaToMuMu2024/dv_mc_Psi2S2mumu.py b/QuarkoniaToMuMu2024/dv_mc_Psi2S2mumu.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f45c9c2f027c4019276f3864117c7e81be0a14c
--- /dev/null
+++ b/QuarkoniaToMuMu2024/dv_mc_Psi2S2mumu.py
@@ -0,0 +1,36 @@
+import os
+import sys
+sys.path.append(os.path.join(
+    os.environ['ANALYSIS_PRODUCTIONS_BASE'], 'QuarkoniaToMuMu2024'))
+from helpers import dv_tree, dv_tree_nopid, dv_mctree, private_combiners
+from DaVinci import Options, make_config
+
+from RecoConf.event_filters import require_pvs
+
+from RecoConf.reconstruction_objects import make_pvs
+
+def main(options: Options):
+    decay_descriptor = {
+        "QQbar": "psi(2S) -> mu+ mu-",
+        "mup": "psi(2S) -> ^mu+ mu-",
+        "mum": "psi(2S) -> mu+ ^mu-",
+    }
+
+    decay_descriptor_mctree = {
+        "QQbar": "psi(2S) ==> mu+ mu-",
+        "mup": "psi(2S) ==> ^mu+ mu-",
+        "mum": "psi(2S) ==> mu+ ^mu-",
+    }
+
+    line_name = 'Hlt2_Psi2SToMuMu'
+    my_tuple_nopid = dv_tree_nopid.tree_template(decay_descriptor, line_name, True, [], private_combiners.make_psi2s())
+    my_tuple = dv_tree.tree_template(decay_descriptor, line_name, True, [])
+    my_filter = dv_tree.line_prefilter(line_name)
+    my_mctuple = dv_mctree.mctree_template(decay_descriptor_mctree)
+    user_algorithms = {
+            'Alg_tuple_nopid': [require_pvs(make_pvs()), my_tuple_nopid],
+            'Alg_tuple': [my_filter, my_tuple],
+            'Alg_mctuple': [my_mctuple],
+            }
+    return make_config(options, user_algorithms)
+
diff --git a/QuarkoniaToMuMu2024/dv_mc_Upsilon1S.py b/QuarkoniaToMuMu2024/dv_mc_Upsilon1S.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7614b867d35df2922b13d306c9b00435dc7ebd6
--- /dev/null
+++ b/QuarkoniaToMuMu2024/dv_mc_Upsilon1S.py
@@ -0,0 +1,35 @@
+import os
+import sys
+sys.path.append(os.path.join(
+    os.environ['ANALYSIS_PRODUCTIONS_BASE'], 'QuarkoniaToMuMu2024'))
+from helpers import dv_tree, dv_tree_nopid, dv_mctree, private_combiners
+from DaVinci import Options, make_config
+
+from RecoConf.event_filters import require_pvs
+
+from RecoConf.reconstruction_objects import make_pvs
+
+def main(options: Options):
+    decay_descriptor = {
+        "QQbar": "Upsilon(1S) -> mu+ mu-",
+        "mup": "Upsilon(1S) -> ^mu+ mu-",
+        "mum": "Upsilon(1S) -> mu+ ^mu-",
+    }
+    decay_descriptor_mctree = {
+        "QQbar": "Upsilon(1S) ==> mu+ mu-",
+        "mup": "Upsilon(1S) ==> ^mu+ mu-",
+        "mum": "Upsilon(1S) ==> mu+ ^mu-",
+    }
+
+    line_name = 'Hlt2BandQ_UpsilonToMuMuEMTF'
+    my_tuple_nopid = dv_tree_nopid.tree_template(decay_descriptor, line_name, True, [], private_combiners.make_upsilon())
+    my_tuple = dv_tree.tree_template(decay_descriptor, line_name, True, [])
+    my_filter = dv_tree.line_prefilter(line_name)
+
+    my_mctuple = dv_mctree.mctree_template(decay_descriptor_mctree)
+    user_algorithms = {
+            'Alg_tuple_nopid': [require_pvs(make_pvs()),  my_tuple_nopid],
+            'Alg_tuple': [my_filter, my_tuple],
+            'Alg_mctuple': [my_mctuple],
+            }
+    return make_config(options, user_algorithms)
diff --git a/QuarkoniaToMuMu2024/dv_mc_Upsilon2S.py b/QuarkoniaToMuMu2024/dv_mc_Upsilon2S.py
new file mode 100644
index 0000000000000000000000000000000000000000..8fd3e9496e6548c6582c1b8694338fca07f94846
--- /dev/null
+++ b/QuarkoniaToMuMu2024/dv_mc_Upsilon2S.py
@@ -0,0 +1,35 @@
+import os
+import sys
+sys.path.append(os.path.join(
+    os.environ['ANALYSIS_PRODUCTIONS_BASE'], 'QuarkoniaToMuMu2024'))
+from helpers import dv_tree, dv_tree_nopid, dv_mctree, private_combiners
+from DaVinci import Options, make_config
+
+from RecoConf.event_filters import require_pvs
+
+from RecoConf.reconstruction_objects import make_pvs
+
+def main(options: Options):
+    decay_descriptor = {
+        "QQbar": "Upsilon(1S) -> mu+ mu-",
+        "mup": "Upsilon(1S) -> ^mu+ mu-",
+        "mum": "Upsilon(1S) -> mu+ ^mu-",
+    }
+    decay_descriptor_mctree = {
+        "QQbar": "Upsilon(2S) ==> mu+ mu-",
+        "mup": "Upsilon(2S) ==> ^mu+ mu-",
+        "mum": "Upsilon(2S) ==> mu+ ^mu-",
+    }
+
+    line_name = 'Hlt2BandQ_UpsilonToMuMuEMTF'
+    my_tuple_nopid = dv_tree_nopid.tree_template(decay_descriptor, line_name, True, [], private_combiners.make_upsilon())
+    my_tuple = dv_tree.tree_template(decay_descriptor, line_name, True, [])
+    my_filter = dv_tree.line_prefilter(line_name)
+
+    my_mctuple = dv_mctree.mctree_template(decay_descriptor_mctree)
+    user_algorithms = {
+            'Alg_tuple_nopid': [require_pvs(make_pvs()),  my_tuple_nopid],
+            'Alg_tuple': [my_filter, my_tuple],
+            'Alg_mctuple': [my_mctuple],
+            }
+    return make_config(options, user_algorithms)
diff --git a/QuarkoniaToMuMu2024/dv_mc_Upsilon3S.py b/QuarkoniaToMuMu2024/dv_mc_Upsilon3S.py
new file mode 100644
index 0000000000000000000000000000000000000000..05fbcd49e5dd9305011b8d37c3ecdcac149a4910
--- /dev/null
+++ b/QuarkoniaToMuMu2024/dv_mc_Upsilon3S.py
@@ -0,0 +1,35 @@
+import os
+import sys
+sys.path.append(os.path.join(
+    os.environ['ANALYSIS_PRODUCTIONS_BASE'], 'QuarkoniaToMuMu2024'))
+from helpers import dv_tree, dv_tree_nopid, dv_mctree, private_combiners
+from DaVinci import Options, make_config
+
+from RecoConf.event_filters import require_pvs
+
+from RecoConf.reconstruction_objects import make_pvs
+
+def main(options: Options):
+    decay_descriptor = {
+        "QQbar": "Upsilon(1S) -> mu+ mu-",
+        "mup": "Upsilon(1S) -> ^mu+ mu-",
+        "mum": "Upsilon(1S) -> mu+ ^mu-",
+    }
+    decay_descriptor_mctree = {
+        "QQbar": "Upsilon(3S) ==> mu+ mu-",
+        "mup": "Upsilon(3S) ==> ^mu+ mu-",
+        "mum": "Upsilon(3S) ==> mu+ ^mu-",
+    }
+
+    line_name = 'Hlt2BandQ_UpsilonToMuMuEMTF'
+    my_tuple_nopid = dv_tree_nopid.tree_template(decay_descriptor, line_name, True, [], private_combiners.make_upsilon())
+    my_tuple = dv_tree.tree_template(decay_descriptor, line_name, True, [])
+    my_filter = dv_tree.line_prefilter(line_name)
+
+    my_mctuple = dv_mctree.mctree_template(decay_descriptor_mctree)
+    user_algorithms = {
+            'Alg_tuple_nopid': [require_pvs(make_pvs()),  my_tuple_nopid],
+            'Alg_tuple': [my_filter, my_tuple],
+            'Alg_mctuple': [my_mctuple],
+            }
+    return make_config(options, user_algorithms)
diff --git a/QuarkoniaToMuMu2024/helpers/dv_mctree.py b/QuarkoniaToMuMu2024/helpers/dv_mctree.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce332790c663bac0722aea2df52f8f40b1af8b9e
--- /dev/null
+++ b/QuarkoniaToMuMu2024/helpers/dv_mctree.py
@@ -0,0 +1,156 @@
+###############################################################################
+# (c) Copyright 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 and process a .xgen file with the new DaVinci configuration.
+rst_title: Run on XGEN files
+rst_description: This example reads and process a `.xgen` file.
+rst_running: lbexec DaVinciExamples.tupling.option_davinci_tupling_from_xgen:main $DAVINCIEXAMPLESROOT/example_data/Gauss_12143001_xgen.yaml
+rst_yaml: ../DaVinciExamples/example_data/Gauss_12143001_xgen.yaml
+"""
+import Functors as F
+import FunTuple.functorcollections as FC
+from FunTuple import FunctorCollection, FunTuple_MCParticles as FuntupleMC
+from PyConf.reading import get_particles, get_pvs, get_rec_summary
+from PyConf.Algorithms import PrintMCTree
+from DaVinci import Options, make_config
+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_mc_particles, get_mc_header
+from PyConf.reading import get_odin  # get_decreports,
+from DecayTreeFitter import DecayTreeFitter
+
+def mctree_template(fields):
+    # FunTuple: define fields (branches)
+    #fields = {
+    #    "QQbar": "J/psi(1S) ==> mu+ mu-",
+    #    "mup": "J/psi(1S) ==> ^mu+ mu-",
+    #    "mum": "J/psi(1S) ==> mu+ ^mu-",
+    #}
+
+    # FunTuple: define input data
+    QQbar2mumu_line = get_mc_particles("/Event/HLT2/MC/Particles")
+
+    pvs = get_pvs()
+
+    #define event level variables
+    odin = get_odin()
+    rec_sum=get_rec_summary()
+
+    event_info = 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"),
+        #"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"),
+    })
+
+
+    MC_MOTHER_ID = lambda gen: F.VALUE_OR(0) @ F.MC_MOTHER(gen, F.PARTICLE_ID)
+    MC_MOTHER_KEY = lambda gen: F.VALUE_OR(-1) @ F.MC_MOTHER(gen, F.OBJECT_KEY)
+
+    # FunTuple: define variables for the J/psi
+    composite_variables = FunctorCollection(
+        {
+            "ETA": F.ETA,
+            "PHI": F.PHI,
+            "TAU": F.MC_LIFETIME,
+            "ORIGIN_VX": F.ORIGIN_VX,
+            "ORIGIN_VY": F.ORIGIN_VY,
+            "ORIGIN_VZ": F.ORIGIN_VZ,
+            "END_VX": F.END_VX,
+            "END_VY": F.END_VY,
+            "END_VZ": F.END_VZ,
+            "M": F.MASS,
+            "FOURMOMENTUM": F.FOURMOMENTUM,
+            "PT": F.PT,
+            "MC_MOTHER_ID": MC_MOTHER_ID(1),
+            "MC_MOTHER_KEY": MC_MOTHER_KEY(1),
+            "MC_GD_MOTHER_ID": MC_MOTHER_ID(2),
+            "MC_GD_MOTHER_KEY": MC_MOTHER_KEY(2),
+            "MC_GD_GD_MOTHER_ID": MC_MOTHER_ID(3),
+            "MC_GD_GD_MOTHER_KEY": MC_MOTHER_KEY(3),
+        }
+     )                                                                    
+
+    # FunTuple: define variables for the muon
+    basic_variables = FunctorCollection(
+        {
+            "ETA": F.ETA,
+            "PHI": F.PHI,
+            "ORIGIN_VX": F.ORIGIN_VX,
+            "ORIGIN_VY": F.ORIGIN_VY,
+            "ORIGIN_VZ": F.ORIGIN_VZ,
+            "M": F.MASS,
+            "FOURMOMENTUM": F.FOURMOMENTUM,
+            "PT": F.PT,
+            "MC_MOTHER_ID": MC_MOTHER_ID(1),
+            "MC_MOTHER_KEY": MC_MOTHER_KEY(1),
+            "MC_GD_MOTHER_ID": MC_MOTHER_ID(2),
+            "MC_GD_MOTHER_KEY": MC_MOTHER_KEY(2),
+            "MC_GD_GD_MOTHER_ID": MC_MOTHER_ID(3),
+            "MC_GD_GD_MOTHER_KEY": MC_MOTHER_KEY(3),
+            "TAU": F.MC_LIFETIME,
+            "END_VX": F.END_VX,
+            "END_VY": F.END_VY,
+            "END_VZ": F.END_VZ,
+        }
+    )
+
+    # FunTuple: associate functor collections to field (branch) name
+    variables = {
+        "QQbar": composite_variables + FC.Kinematics(),
+        "mup": basic_variables + FC.Kinematics(),
+        "mum": basic_variables + FC.Kinematics(),
+    }
+
+    # FunTuple: define event-level variables using functor collections
+    mc_header = get_mc_header(extra_inputs=[QQbar2mumu_line])
+    evt_vars = event_info
+    #evt_vars = FC.MCPrimaries(mc_header=mc_header) + event_info
+
+    #printMC = PrintMCTree(
+    #    MCParticles=QQbar2mumu_line, ParticleNames=["J/psi(1S)"], OutputLevel=4
+    #)
+
+    tuple_QQbar2mumu = FuntupleMC(
+        name="MCDecayTreeTuple",
+        tuple_name="MCDecayTree",
+        fields=fields,
+        variables=variables,
+        event_variables=evt_vars,
+        inputs=QQbar2mumu_line,
+    )
+
+    return tuple_QQbar2mumu
+
diff --git a/QuarkoniaToMuMu2024/helpers/dv_tree.py b/QuarkoniaToMuMu2024/helpers/dv_tree.py
new file mode 100644
index 0000000000000000000000000000000000000000..8032c9a904ef569381945dca6cc10831d00b3eda
--- /dev/null
+++ b/QuarkoniaToMuMu2024/helpers/dv_tree.py
@@ -0,0 +1,514 @@
+###############################################################################
+# (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
+
+
+
+_basic = "basic"
+_composite = "composite"
+_toplevel = "toplevel"
+
+#def all_variables(pvs, dtf, mctruth, ptype, candidates=None, ftAlg=None):
+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)
+    all_vars += FC.MCKinematics(mctruth_alg=mctruth)
+    all_vars += FC.MCHierarchy(mctruth_alg=mctruth)
+    #all_vars += FC.MCPrimaryVertexInfo(mctruth_alg=mctruth)
+    #Hlt1_decisions = ["Hlt1TrackMVADecision", "Hlt1TwoTrackMVADecision"]
+    #if candidates:
+        #all_vars += FC.HltTisTos(
+            #selection_type="Hlt", trigger_lines=Hlt1_decisions, data=candidates
+        #)
+    #if comp:
+    #    all_vars += FC.MCVertexInfo(mctruth_alg=mctruth)
+    #if top:
+    #    all_vars += FC.MCPromptDecay(mctruth_alg=mctruth)
+    
+    #
+    # FTAlg not yet implemented
+    # For Track isolation see weightedrelations_trackvariables
+    #
+    # Now all other functors
+    
+    # ALL : Not useful for tupling
+    
+    if comp:
+        all_vars.update({"ALV": F.ALV(Child1=1, Child2=2)})
+        
+        all_vars.update({"BKGCAT": mctruth.BkgCat})
+        
+    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)})
+    # When storing variable length array one can
+    # give a custom branch name for the index.
+    # This can be achieved by enclosing custom index
+    # name within square brackets (see code below).
+    # The branch name ("nPV") will correspond to the
+    # index of the PV. If no index branch name given i.e.
+    # all_vars.update({ 'ALLPVX'] the default "indx" is used.
+    #all_vars.update({"ALLPVX[nPVs]": F.ALLPVX(pvs)})
+    #all_vars.update({"ALLPVY[nPVs]": F.ALLPVY(pvs)})
+    #all_vars.update({"ALLPVZ[nPVs]": F.ALLPVZ(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[nPVs]": F.ALLPV_FD(pvs)})
+        #all_vars.update({"ALLPV_IP[nPVs]": F.ALLPV_IP(pvs)})
+        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 basic: all_vars.update({ 'CLOSESTTOBEAM' : F.CLOSESTTOBEAM # 'Track__ClosestToBeamState' object has no attribute 'to_json'
+    # COMB
+    # if basic: all_vars.update({ 'COV' : F.COV # 'Track__Covariance' object has no attribute 'to_json'
+
+    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})
+        # 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})
+
+    #if dtf:
+    #    all_vars.update({"DTF_PT": dtf(F.PT)})
+    #    all_vars.update({"DTF_BPVIPCHI2": dtf(F.BPVIPCHI2(pvs))})
+        
+    #if top and dtf:
+    #    all_vars.update({"DTF_NITER": dtf.NITER})
+    #    all_vars.update({"DTF_CHI2": dtf.CHI2})
+    #    all_vars.update({"DTF_NDOF": dtf.NDOF})
+    #    all_vars.update({"DTF_CHI2DOF": dtf.CHI2DOF})
+         
+    #if comp and dtf:
+    #    all_vars.update({"DTF_MASS": dtf.MASS})
+    #    all_vars.update({"DTF_MASSERR": dtf.MASSERR})
+    #    all_vars.update({"DTF_P": dtf.P})
+    #    all_vars.update({"DTF_PERR": dtf.PERR})
+    #    all_vars.update({"DTF_TAU": dtf.TAU})
+    #    all_vars.update({"DTF_TAUERR": dtf.TAUERR})
+    #    all_vars.update({"DTF_FD": dtf.FD})
+    #    all_vars.update({"DTF_FDERR": dtf.FDERR})
+
+    all_vars.update({"MASS": F.MASS})
+    #if top:  # B
+    #    all_vars.update({"MASSWITHHYPOTHESES": F.MASSWITHHYPOTHESES((939.0, 939.0))})
+    #elif comp:  # Ds
+    #    all_vars.update(
+    #        {"MASSWITHHYPOTHESES": F.MASSWITHHYPOTHESES((493.7, 493.7, 139.6))}
+    #    )
+    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.
+         
+    # 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({"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})
+
+    # duplicated from FC        all_vars.update({ 'ORIGIN_VX' : mctruth(F.ORIGIN_VX) })
+    # duplicated from FC        all_vars.update({ 'ORIGIN_VY' : mctruth(F.ORIGIN_VY) })
+    # duplicated from FC        all_vars.update({ 'ORIGIN_VZ' : mctruth(F.ORIGIN_VZ) })
+    
+    # duplicated from FC        all_vars.update({"P" : F.P})
+    # duplicated from FC        all_vars.update({"PARTICLE_ID" : F.PARTICLE_ID})
+    all_vars.update({"PHI": F.PHI})
+    
+    # duplicated from FC    if basic:
+    # duplicated from FC        all_vars.update({"PID_E" : F.PID_E})
+    # duplicated from FC        all_vars.update({"PID_K" : F.PID_K})
+    # duplicated from FC        all_vars.update({"PID_MU" : F.PID_MU})
+    # duplicated from FC        all_vars.update({"PID_P" : F.PID_P})
+    # duplicated from FC        all_vars.update({"PID_PI" : F.PID_PI})
+    # duplicated from FC        #all_vars.update({"PROBNN_D" : F.PROBNN_D})
+    # duplicated from FC        all_vars.update({"PROBNN_E" : F.PROBNN_E})
+    # duplicated from FC        all_vars.update({"PROBNN_GHOST" : F.PROBNN_GHOST})
+    # duplicated from FC        all_vars.update({"PROBNN_K" : F.PROBNN_K})
+    # duplicated from FC        all_vars.update({"PROBNN_MU" : F.PROBNN_MU})
+    # duplicated from FC        all_vars.update({"PROBNN_P" : F.PROBNN_P})
+    # duplicated from FC        all_vars.update({"PROBNN_PI" : F.PROBNN_PI})
+
+    # duplicated from FC    all_vars.update({ 'PT' : F.PT })
+    # duplicated from FC    all_vars.update({ 'PX' : F.PX })
+    # duplicated from FC    all_vars.update({ 'PY' : F.PY })
+    # duplicated from FC    all_vars.update({ 'PZ' : F.PZ })
+    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, hlt1lines):
+    """
+    event variables
+    """
+     
+    evt_vars = FunctorCollection({})
+    evt_vars += FC.EventInfo()
+    
+    evt_vars += FC.SelectionInfo(selection_type="Hlt2", trigger_lines=lines)
+    evt_vars += FC.SelectionInfo(selection_type="Hlt1", trigger_lines=hlt1lines)
+    # 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 tree_template(decay_descriptor, line_name, isturbo, Hlt2_decisions):
+    evtpath_prefix = "/Event/Spruce/"
+    if isturbo:
+        evtpath_prefix = "/Event/HLT2/"
+
+    QQbar2mumu_data = get_particles(evtpath_prefix+f"{line_name}/Particles")
+    #QQbar2mumu_data = make_jpsi(minPIDmu=-999) 
+
+    pvs = get_pvs()
+
+    Hlt1_decisions = [
+# Global line
+        "Hlt1GlobalDecision", "Hlt1PhysDecision", 
+# Physics lines
+        "Hlt1TrackMVADecision", "Hlt1TwoTrackMVADecision", "Hlt1D2KKDecision", "Hlt1D2KPiDecision", "Hlt1D2PiPiDecision", "Hlt1Dst2D0PiDecision", "Hlt1KsToPiPiDecision", "Hlt1KsToPiPiDoubleMuonMisIDDecision", "Hlt1TwoTrackKsDecision", "Hlt1TwoKsDecision", "Hlt1LambdaLLDetachedTrackDecision", "Hlt1XiOmegaLLLDecision", "Hlt1SingleHighPtMuonDecision", "Hlt1SingleHighPtMuonNoMuIDDecision", "Hlt1DiMuonHighMassDecision", "Hlt1DiMuonDisplacedDecision", "Hlt1DiMuonSoftDecision", "Hlt1TrackMuonMVADecision", "Hlt1DiMuonNoIP_SSDecision", "Hlt1DiMuonDrellYan_VLowMassDecision", "Hlt1DiMuonDrellYan_VLowMass_SSDecision", "Hlt1DiMuonDrellYanDecision", "Hlt1DiMuonDrellYan_SSDecision", "Hlt1DetJpsiToMuMuPosTagLineDecision", "Hlt1DetJpsiToMuMuNegTagLineDecision", "Hlt1TrackElectronMVADecision", "Hlt1SingleHighPtElectronDecision", "Hlt1DiElectronDisplacedDecision", "Hlt1SingleHighEtDecision", "Hlt1DiPhotonHighMassDecision", "Hlt1Pi02GammaGammaDecision", "Hlt1DiElectronHighMass_SSDecision", "Hlt1DiElectronHighMassDecision", "Hlt1DiMuonNoIPDecision"
+## alignment lines
+#        "Hlt1RICH1AlignmentDecision", "Hlt1RICH2AlignmentDecision", "Hlt1D2KPiAlignmentDecision", "Hlt1Dst2D0PiAlignmentDecision", "Hlt1MaterialVertexSeedsDownstreamzDecision", "Hlt1MaterialVertexSeeds_DWFSDecision", "Hlt1DiMuonHighMassAlignmentDecision", "Hlt1DiMuonJpsiMassAlignmentDecision", "Hlt1OneMuonTrackLineDecision", "Hlt1BeamGasDecision",
+## smog2 lines
+#        "Hlt1SMOG2D2KpiDecision", "Hlt1VeloMicroBiasDecision", "Hlt1SMOG2etacToppDecision", "Hlt1SMOG2KsTopipiDecision", "Hlt1SMOG2etacToppDecision", "Hlt1SMOG2KsTopipiDecision", "Hlt1SMOG22BodyGenericDecision", "Hlt1SMOG22BodyGenericLowPtDecision", "Hlt1SMOG2SingleTrackVeryHighPtDecision", "Hlt1SMOG2SingleTrackHighPtDecision", "Hlt1SMOG2DiMuonHighMassDecision", "Hlt1SMOG2SingleMuonDecision", "Hlt1SMOG2L0ToppiDecision",
+## bgi lines
+#         "Hlt1BGIPseudoPVsNoBeamDecision", "Hlt1BGIPseudoPVsBeamOneDecision", "Hlt1BGIPseudoPVsBeamTwoDecision", "Hlt1BGIPseudoPVsUpBeamBeamDecision", "Hlt1BGIPseudoPVsDownBeamBeamDecision", "Hlt1BGIPseudoPVsIRBeamBeamDecision", "Hlt1BGIPVsCylNoBeamDecision", "Hlt1BGIPVsCylBeamOneDecision", "Hlt1BGIPVsCylBeamTwoDecision", "Hlt1BGIPVsCylUpBeamBeamDecision", "Hlt1BGIPVsCylDownBeamBeamDecision", "Hlt1BGIPVsCylIRBeamBeamDecision", "Hlt1BGIVeloClustersMicroBiasDecision", "Hlt1BGICaloDigitsDecision", "Hlt1BGIPlumeActivityDecision",
+## setup hlt1 nodes lines
+#        "Hlt1ODINCalibDecision", "Hlt1TAEPassthroughDecision", "Hlt1ErrorBankDecision", "Hlt1VeloMicroBiasVeloClosingDecision", "Hlt1GECPassthroughDecision", "Hlt1SMOG2BENoBiasDecision", "Hlt1SMOG2PassThroughLowMult5Decision", "Hlt1SMOG2BELowMultElectronsDecision", "Hlt1SMOG2MinimumBiasDecision", "Hlt1PassthroughPVinSMOG2Decision"
+     ]
+
+
+    composite_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,
+        #"ETA": F.ETA,
+        #"PHI": F.PHI,
+        #"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,
+        "END_VY_ERR":F.SQRT @ F.CALL(1,1) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "END_VX_ERR":F.SQRT @ F.CALL(0,0) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "BPVZ_ERR": F.SQRT @ F.CALL(2,2) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "BPVY_ERR": F.SQRT @ F.CALL(1,1) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "BPVX_ERR": F.SQRT @ F.CALL(0,0) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        #"TAU": F.BPVLTIME(pvs),
+        #"BPVFDCHI2": F.BPVFDCHI2(pvs),
+        #"BPVIPCHI2": F.BPVIPCHI2(pvs)
+    })
+
+    #composite_variables += Kinematics()
+    #composite_variables += ParticleID(extra_info=True)
+    composite_variables += HltTisTos( selection_type="Hlt1", trigger_lines=Hlt1_decisions, data=QQbar2mumu_data)
+    if not isturbo:
+        composite_variables += HltTisTos( selection_type="Hlt2", trigger_lines=Hlt2_decisions, data=QQbar2mumu_data)
+
+    daughter_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,
+        #"PHI": F.PHI,
+        #"Track_CHI2NDOF": F.CHI2DOF,
+        #"ProbNNmu": F.PROBNN_MU,
+        #"PIDmu": F.PID_MU,
+        #"IsMuon": F.ISMUON,
+        #"GhostProb": F.GHOSTPROB,
+        #"FOURMOMENTUM": F.FOURMOMENTUM,
+        "PERR": F.SQRT @ F.PERR2,
+        "PZERR": F.SQRT @ F.CALL(2,2) @ F.THREE_MOM_COV_MATRIX,
+        "TXERR": F.SQRT @ F.CALL(2,2) @ F.TRACK_COVARIANCE @ F.STATE_AT("FirstMeasurement")@ F.TRACK,
+        "TYERR": F.SQRT @ F.CALL(3,3) @ F.TRACK_COVARIANCE @ F.STATE_AT("FirstMeasurement")@ F.TRACK,
+        "COVTXTY": F.CALL(2,3) @ F.TRACK_COVARIANCE @ F.STATE_AT("FirstMeasurement")@ F.TRACK,
+    })
+
+    #daughter_variables += Kinematics()
+    #daughter_variables += ParticleID(extra_info=True)
+
+#    line_prefilter = create_lines_filter(name=f"HLT_PASS{line_name}", lines=[line_name])
+
+    #define event level variables
+    odin = get_odin()
+    decreports = None
+    rec_sum=get_rec_summary()
+    event_info = event_variables(pvs, odin, decreports, [line_name], Hlt1_decisions) + 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),
+    })
+
+    # get trueid bkgcat info
+    MC_TRUTH = MCTruthAndBkgCat(QQbar2mumu_data, name='MCTruthAndBkgCat_'+line_name+"_{hash}")
+    MCMOTHER_ID = lambda n: F.VALUE_OR(0) @ MC_TRUTH(F.MC_MOTHER(n, F.PARTICLE_ID))
+    MCMOTHER_KEY = lambda n: F.VALUE_OR(-1) @ MC_TRUTH(F.MC_MOTHER(n, F.OBJECT_KEY))
+
+    trueid_bkgcat_info = FunctorCollection({
+        #"TRUEID": F.VALUE_OR(0) @ MC_TRUTH(F.PARTICLE_ID),
+        "TRUEKEY": F.VALUE_OR(-1) @ MC_TRUTH(F.OBJECT_KEY),
+        #"TRUEPT": MC_TRUTH(F.PT),
+        #"TRUEPX": MC_TRUTH(F.PX),
+        #"TRUEPY": MC_TRUTH(F.PY),
+        #"TRUEPZ": MC_TRUTH(F.PZ),
+        #"TRUEENERGY": MC_TRUTH(F.ENERGY),
+        #"TRUEP": MC_TRUTH(F.P),
+        "TRUEM": MC_TRUTH(F.MASS),
+        "TRUEETA": MC_TRUTH(F.ETA),
+        "TRUEPHI": MC_TRUTH(F.PHI),
+        "TRUEFOURMOMENTUM": MC_TRUTH(F.FOURMOMENTUM),
+        #"MC_MOTHER_ID": MCMOTHER_ID(1),
+        #"MC_MOTHER_KEY": MCMOTHER_KEY(1),
+        #"MC_GD_MOTHER_ID": MCMOTHER_ID(2),
+        #"MC_GD_MOTHER_KEY": MCMOTHER_KEY(2),
+        #"MC_GD_GD_MOTHER_ID": MCMOTHER_ID(3),
+        #"MC_GD_GD_MOTHER_KEY": MCMOTHER_KEY(3),
+        "TRUEORIGIN_VX": MC_TRUTH(F.ORIGIN_VX),
+        "TRUEORIGIN_VY": MC_TRUTH(F.ORIGIN_VY),
+        "TRUEORIGIN_VZ": MC_TRUTH(F.ORIGIN_VZ),
+        #"TRUEEND_VX": MC_TRUTH(F.END_VX),
+        #"TRUEEND_VY": MC_TRUTH(F.END_VY),
+        #"TRUEEND_VZ": MC_TRUTH(F.END_VZ),
+        "BKGCAT": MC_TRUTH.BkgCat,
+    })
+
+    variables = {
+        "QQbar": composite_variables + all_variables(pvs, MC_TRUTH, _composite) + trueid_bkgcat_info,
+        "mup": daughter_variables + all_variables(pvs, MC_TRUTH, _basic) + trueid_bkgcat_info,
+        "mum": daughter_variables + all_variables(pvs, MC_TRUTH, _basic) + trueid_bkgcat_info, 
+    }
+
+    #define FunTuple instance
+    my_tuple = Funtuple(
+        name=line_name,
+        tuple_name="DecayTree",
+        fields=decay_descriptor,
+        variables=variables,
+        event_variables=event_info,
+        store_multiple_cand_info = True,
+        inputs=QQbar2mumu_data)
+
+    return my_tuple
+
+def line_prefilter(line_name):
+    return create_lines_filter(name=f"HLT_PASS{line_name}", lines=[line_name])
+
diff --git a/QuarkoniaToMuMu2024/helpers/dv_tree_nopid.py b/QuarkoniaToMuMu2024/helpers/dv_tree_nopid.py
new file mode 100644
index 0000000000000000000000000000000000000000..5bfe4a88ee5754562825f0219e4e48dc4f7d7f4d
--- /dev/null
+++ b/QuarkoniaToMuMu2024/helpers/dv_tree_nopid.py
@@ -0,0 +1,514 @@
+###############################################################################
+# (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
+
+
+
+_basic = "basic"
+_composite = "composite"
+_toplevel = "toplevel"
+
+#def all_variables(pvs, dtf, mctruth, ptype, candidates=None, ftAlg=None):
+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)
+    all_vars += FC.MCKinematics(mctruth_alg=mctruth)
+    all_vars += FC.MCHierarchy(mctruth_alg=mctruth)
+    #all_vars += FC.MCPrimaryVertexInfo(mctruth_alg=mctruth)
+    #Hlt1_decisions = ["Hlt1TrackMVADecision", "Hlt1TwoTrackMVADecision"]
+    #if candidates:
+        #all_vars += FC.HltTisTos(
+            #selection_type="Hlt", trigger_lines=Hlt1_decisions, data=candidates
+        #)
+    #if comp:
+    #    all_vars += FC.MCVertexInfo(mctruth_alg=mctruth)
+    #if top:
+    #    all_vars += FC.MCPromptDecay(mctruth_alg=mctruth)
+    
+    #
+    # FTAlg not yet implemented
+    # For Track isolation see weightedrelations_trackvariables
+    #
+    # Now all other functors
+    
+    # ALL : Not useful for tupling
+    
+    if comp:
+        all_vars.update({"ALV": F.ALV(Child1=1, Child2=2)})
+        
+        all_vars.update({"BKGCAT": mctruth.BkgCat})
+        
+    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)})
+    # When storing variable length array one can
+    # give a custom branch name for the index.
+    # This can be achieved by enclosing custom index
+    # name within square brackets (see code below).
+    # The branch name ("nPV") will correspond to the
+    # index of the PV. If no index branch name given i.e.
+    # all_vars.update({ 'ALLPVX'] the default "indx" is used.
+    #all_vars.update({"ALLPVX[nPVs]": F.ALLPVX(pvs)})
+    #all_vars.update({"ALLPVY[nPVs]": F.ALLPVY(pvs)})
+    #all_vars.update({"ALLPVZ[nPVs]": F.ALLPVZ(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[nPVs]": F.ALLPV_FD(pvs)})
+        #all_vars.update({"ALLPV_IP[nPVs]": F.ALLPV_IP(pvs)})
+        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 basic: all_vars.update({ 'CLOSESTTOBEAM' : F.CLOSESTTOBEAM # 'Track__ClosestToBeamState' object has no attribute 'to_json'
+    # COMB
+    # if basic: all_vars.update({ 'COV' : F.COV # 'Track__Covariance' object has no attribute 'to_json'
+
+    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})
+        # 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})
+
+    #if dtf:
+    #    all_vars.update({"DTF_PT": dtf(F.PT)})
+    #    all_vars.update({"DTF_BPVIPCHI2": dtf(F.BPVIPCHI2(pvs))})
+        
+    #if top and dtf:
+    #    all_vars.update({"DTF_NITER": dtf.NITER})
+    #    all_vars.update({"DTF_CHI2": dtf.CHI2})
+    #    all_vars.update({"DTF_NDOF": dtf.NDOF})
+    #    all_vars.update({"DTF_CHI2DOF": dtf.CHI2DOF})
+         
+    #if comp and dtf:
+    #    all_vars.update({"DTF_MASS": dtf.MASS})
+    #    all_vars.update({"DTF_MASSERR": dtf.MASSERR})
+    #    all_vars.update({"DTF_P": dtf.P})
+    #    all_vars.update({"DTF_PERR": dtf.PERR})
+    #    all_vars.update({"DTF_TAU": dtf.TAU})
+    #    all_vars.update({"DTF_TAUERR": dtf.TAUERR})
+    #    all_vars.update({"DTF_FD": dtf.FD})
+    #    all_vars.update({"DTF_FDERR": dtf.FDERR})
+
+    all_vars.update({"MASS": F.MASS})
+    #if top:  # B
+    #    all_vars.update({"MASSWITHHYPOTHESES": F.MASSWITHHYPOTHESES((939.0, 939.0))})
+    #elif comp:  # Ds
+    #    all_vars.update(
+    #        {"MASSWITHHYPOTHESES": F.MASSWITHHYPOTHESES((493.7, 493.7, 139.6))}
+    #    )
+    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.
+         
+    # 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({"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})
+
+    # duplicated from FC        all_vars.update({ 'ORIGIN_VX' : mctruth(F.ORIGIN_VX) })
+    # duplicated from FC        all_vars.update({ 'ORIGIN_VY' : mctruth(F.ORIGIN_VY) })
+    # duplicated from FC        all_vars.update({ 'ORIGIN_VZ' : mctruth(F.ORIGIN_VZ) })
+    
+    # duplicated from FC        all_vars.update({"P" : F.P})
+    # duplicated from FC        all_vars.update({"PARTICLE_ID" : F.PARTICLE_ID})
+    all_vars.update({"PHI": F.PHI})
+    
+    # duplicated from FC    if basic:
+    # duplicated from FC        all_vars.update({"PID_E" : F.PID_E})
+    # duplicated from FC        all_vars.update({"PID_K" : F.PID_K})
+    # duplicated from FC        all_vars.update({"PID_MU" : F.PID_MU})
+    # duplicated from FC        all_vars.update({"PID_P" : F.PID_P})
+    # duplicated from FC        all_vars.update({"PID_PI" : F.PID_PI})
+    # duplicated from FC        #all_vars.update({"PROBNN_D" : F.PROBNN_D})
+    # duplicated from FC        all_vars.update({"PROBNN_E" : F.PROBNN_E})
+    # duplicated from FC        all_vars.update({"PROBNN_GHOST" : F.PROBNN_GHOST})
+    # duplicated from FC        all_vars.update({"PROBNN_K" : F.PROBNN_K})
+    # duplicated from FC        all_vars.update({"PROBNN_MU" : F.PROBNN_MU})
+    # duplicated from FC        all_vars.update({"PROBNN_P" : F.PROBNN_P})
+    # duplicated from FC        all_vars.update({"PROBNN_PI" : F.PROBNN_PI})
+
+    # duplicated from FC    all_vars.update({ 'PT' : F.PT })
+    # duplicated from FC    all_vars.update({ 'PX' : F.PX })
+    # duplicated from FC    all_vars.update({ 'PY' : F.PY })
+    # duplicated from FC    all_vars.update({ 'PZ' : F.PZ })
+    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, hlt1lines):
+    """
+    event variables
+    """
+     
+    evt_vars = FunctorCollection({})
+    evt_vars += FC.EventInfo()
+    
+    evt_vars += FC.SelectionInfo(selection_type="Hlt2", trigger_lines=lines)
+    evt_vars += FC.SelectionInfo(selection_type="Hlt1", trigger_lines=hlt1lines)
+    # 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 tree_template(decay_descriptor, line_name, isturbo, Hlt2_decisions, QQbar2mumu_data):
+    evtpath_prefix = "/Event/Spruce/"
+    if isturbo:
+        evtpath_prefix = "/Event/HLT2/"
+
+    #QQbar2mumu_data = get_particles(evtpath_prefix+f"{line_name}/Particles")
+    #QQbar2mumu_data = make_jpsi(minPIDmu=-999) 
+
+    pvs = get_pvs()
+
+    Hlt1_decisions = [
+# Global line
+        "Hlt1GlobalDecision", "Hlt1PhysDecision", 
+# Physics lines
+        "Hlt1TrackMVADecision", "Hlt1TwoTrackMVADecision", "Hlt1D2KKDecision", "Hlt1D2KPiDecision", "Hlt1D2PiPiDecision", "Hlt1Dst2D0PiDecision", "Hlt1KsToPiPiDecision", "Hlt1KsToPiPiDoubleMuonMisIDDecision", "Hlt1TwoTrackKsDecision", "Hlt1TwoKsDecision", "Hlt1LambdaLLDetachedTrackDecision", "Hlt1XiOmegaLLLDecision", "Hlt1SingleHighPtMuonDecision", "Hlt1SingleHighPtMuonNoMuIDDecision", "Hlt1DiMuonHighMassDecision", "Hlt1DiMuonDisplacedDecision", "Hlt1DiMuonSoftDecision", "Hlt1TrackMuonMVADecision", "Hlt1DiMuonNoIP_SSDecision", "Hlt1DiMuonDrellYan_VLowMassDecision", "Hlt1DiMuonDrellYan_VLowMass_SSDecision", "Hlt1DiMuonDrellYanDecision", "Hlt1DiMuonDrellYan_SSDecision", "Hlt1DetJpsiToMuMuPosTagLineDecision", "Hlt1DetJpsiToMuMuNegTagLineDecision", "Hlt1TrackElectronMVADecision", "Hlt1SingleHighPtElectronDecision", "Hlt1DiElectronDisplacedDecision", "Hlt1SingleHighEtDecision", "Hlt1DiPhotonHighMassDecision", "Hlt1Pi02GammaGammaDecision", "Hlt1DiElectronHighMass_SSDecision", "Hlt1DiElectronHighMassDecision", "Hlt1DiMuonNoIPDecision"
+## alignment lines
+#        "Hlt1RICH1AlignmentDecision", "Hlt1RICH2AlignmentDecision", "Hlt1D2KPiAlignmentDecision", "Hlt1Dst2D0PiAlignmentDecision", "Hlt1MaterialVertexSeedsDownstreamzDecision", "Hlt1MaterialVertexSeeds_DWFSDecision", "Hlt1DiMuonHighMassAlignmentDecision", "Hlt1DiMuonJpsiMassAlignmentDecision", "Hlt1OneMuonTrackLineDecision", "Hlt1BeamGasDecision",
+## smog2 lines
+#        "Hlt1SMOG2D2KpiDecision", "Hlt1VeloMicroBiasDecision", "Hlt1SMOG2etacToppDecision", "Hlt1SMOG2KsTopipiDecision", "Hlt1SMOG2etacToppDecision", "Hlt1SMOG2KsTopipiDecision", "Hlt1SMOG22BodyGenericDecision", "Hlt1SMOG22BodyGenericLowPtDecision", "Hlt1SMOG2SingleTrackVeryHighPtDecision", "Hlt1SMOG2SingleTrackHighPtDecision", "Hlt1SMOG2DiMuonHighMassDecision", "Hlt1SMOG2SingleMuonDecision", "Hlt1SMOG2L0ToppiDecision",
+## bgi lines
+#         "Hlt1BGIPseudoPVsNoBeamDecision", "Hlt1BGIPseudoPVsBeamOneDecision", "Hlt1BGIPseudoPVsBeamTwoDecision", "Hlt1BGIPseudoPVsUpBeamBeamDecision", "Hlt1BGIPseudoPVsDownBeamBeamDecision", "Hlt1BGIPseudoPVsIRBeamBeamDecision", "Hlt1BGIPVsCylNoBeamDecision", "Hlt1BGIPVsCylBeamOneDecision", "Hlt1BGIPVsCylBeamTwoDecision", "Hlt1BGIPVsCylUpBeamBeamDecision", "Hlt1BGIPVsCylDownBeamBeamDecision", "Hlt1BGIPVsCylIRBeamBeamDecision", "Hlt1BGIVeloClustersMicroBiasDecision", "Hlt1BGICaloDigitsDecision", "Hlt1BGIPlumeActivityDecision",
+## setup hlt1 nodes lines
+#        "Hlt1ODINCalibDecision", "Hlt1TAEPassthroughDecision", "Hlt1ErrorBankDecision", "Hlt1VeloMicroBiasVeloClosingDecision", "Hlt1GECPassthroughDecision", "Hlt1SMOG2BENoBiasDecision", "Hlt1SMOG2PassThroughLowMult5Decision", "Hlt1SMOG2BELowMultElectronsDecision", "Hlt1SMOG2MinimumBiasDecision", "Hlt1PassthroughPVinSMOG2Decision"
+     ]
+
+
+    composite_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,
+        #"ETA": F.ETA,
+        #"PHI": F.PHI,
+        #"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,
+        "END_VY_ERR":F.SQRT @ F.CALL(1,1) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "END_VX_ERR":F.SQRT @ F.CALL(0,0) @ F.POS_COV_MATRIX @ F.ENDVERTEX,
+        "BPVZ_ERR": F.SQRT @ F.CALL(2,2) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "BPVY_ERR": F.SQRT @ F.CALL(1,1) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        "BPVX_ERR": F.SQRT @ F.CALL(0,0) @ F.POS_COV_MATRIX @ F.BPV(pvs),
+        #"TAU": F.BPVLTIME(pvs),
+        #"BPVFDCHI2": F.BPVFDCHI2(pvs),
+        #"BPVIPCHI2": F.BPVIPCHI2(pvs)
+    })
+
+    #composite_variables += Kinematics()
+    #composite_variables += ParticleID(extra_info=True)
+    composite_variables += HltTisTos( selection_type="Hlt1", trigger_lines=Hlt1_decisions, data=QQbar2mumu_data)
+    if not isturbo:
+        composite_variables += HltTisTos( selection_type="Hlt2", trigger_lines=Hlt2_decisions, data=QQbar2mumu_data)
+
+    daughter_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,
+        #"PHI": F.PHI,
+        #"Track_CHI2NDOF": F.CHI2DOF,
+        #"ProbNNmu": F.PROBNN_MU,
+        #"PIDmu": F.PID_MU,
+        #"IsMuon": F.ISMUON,
+        #"GhostProb": F.GHOSTPROB,
+        #"FOURMOMENTUM": F.FOURMOMENTUM,
+        "PERR": F.SQRT @ F.PERR2,
+        "PZERR": F.SQRT @ F.CALL(2,2) @ F.THREE_MOM_COV_MATRIX,
+        "TXERR": F.SQRT @ F.CALL(2,2) @ F.TRACK_COVARIANCE @ F.STATE_AT("FirstMeasurement")@ F.TRACK,
+        "TYERR": F.SQRT @ F.CALL(3,3) @ F.TRACK_COVARIANCE @ F.STATE_AT("FirstMeasurement")@ F.TRACK,
+        "COVTXTY": F.CALL(2,3) @ F.TRACK_COVARIANCE @ F.STATE_AT("FirstMeasurement")@ F.TRACK,
+    })
+
+    #daughter_variables += Kinematics()
+    #daughter_variables += ParticleID(extra_info=True)
+
+#    line_prefilter = create_lines_filter(name=f"HLT_PASS{line_name}", lines=[line_name])
+
+    #define event level variables
+    odin = get_odin()
+    decreports = None
+    rec_sum=get_rec_summary()
+    event_info = event_variables(pvs, odin, decreports, [line_name], Hlt1_decisions) + 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),
+    })
+
+    # get trueid bkgcat info
+    MC_TRUTH = MCTruthAndBkgCat(QQbar2mumu_data, name='MCTruthAndBkgCat_'+line_name+"_{hash}")
+    MCMOTHER_ID = lambda n: F.VALUE_OR(0) @ MC_TRUTH(F.MC_MOTHER(n, F.PARTICLE_ID))
+    MCMOTHER_KEY = lambda n: F.VALUE_OR(-1) @ MC_TRUTH(F.MC_MOTHER(n, F.OBJECT_KEY))
+
+    trueid_bkgcat_info = FunctorCollection({
+        #"TRUEID": F.VALUE_OR(0) @ MC_TRUTH(F.PARTICLE_ID),
+        "TRUEKEY": F.VALUE_OR(-1) @ MC_TRUTH(F.OBJECT_KEY),
+        #"TRUEPT": MC_TRUTH(F.PT),
+        #"TRUEPX": MC_TRUTH(F.PX),
+        #"TRUEPY": MC_TRUTH(F.PY),
+        #"TRUEPZ": MC_TRUTH(F.PZ),
+        #"TRUEENERGY": MC_TRUTH(F.ENERGY),
+        #"TRUEP": MC_TRUTH(F.P),
+        "TRUEM": MC_TRUTH(F.MASS),
+        "TRUEETA": MC_TRUTH(F.ETA),
+        "TRUEPHI": MC_TRUTH(F.PHI),
+        "TRUEFOURMOMENTUM": MC_TRUTH(F.FOURMOMENTUM),
+        #"MC_MOTHER_ID": MCMOTHER_ID(1),
+        #"MC_MOTHER_KEY": MCMOTHER_KEY(1),
+        #"MC_GD_MOTHER_ID": MCMOTHER_ID(2),
+        #"MC_GD_MOTHER_KEY": MCMOTHER_KEY(2),
+        #"MC_GD_GD_MOTHER_ID": MCMOTHER_ID(3),
+        #"MC_GD_GD_MOTHER_KEY": MCMOTHER_KEY(3),
+        "TRUEORIGIN_VX": MC_TRUTH(F.ORIGIN_VX),
+        "TRUEORIGIN_VY": MC_TRUTH(F.ORIGIN_VY),
+        "TRUEORIGIN_VZ": MC_TRUTH(F.ORIGIN_VZ),
+        #"TRUEEND_VX": MC_TRUTH(F.END_VX),
+        #"TRUEEND_VY": MC_TRUTH(F.END_VY),
+        #"TRUEEND_VZ": MC_TRUTH(F.END_VZ),
+        "BKGCAT": MC_TRUTH.BkgCat,
+    })
+
+    variables = {
+        "QQbar": composite_variables + all_variables(pvs, MC_TRUTH, _composite) + trueid_bkgcat_info,
+        "mup": daughter_variables + all_variables(pvs, MC_TRUTH, _basic) + trueid_bkgcat_info,
+        "mum": daughter_variables + all_variables(pvs, MC_TRUTH, _basic) + trueid_bkgcat_info, 
+    }
+
+    #define FunTuple instance
+    my_tuple = Funtuple(
+        name=line_name+"_nopid",
+        tuple_name="DecayTree",
+        fields=decay_descriptor,
+        variables=variables,
+        event_variables=event_info,
+        store_multiple_cand_info = True,
+        inputs=QQbar2mumu_data)
+
+    return my_tuple
+
+#def line_prefilter(line_name):
+#    return create_lines_filter(name=f"HLT_PASS{line_name}", lines=[line_name])
+
diff --git a/QuarkoniaToMuMu2024/helpers/private_combiners.py b/QuarkoniaToMuMu2024/helpers/private_combiners.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa0a5ad1c624751738dc2ead505a327db363ef0d
--- /dev/null
+++ b/QuarkoniaToMuMu2024/helpers/private_combiners.py
@@ -0,0 +1,207 @@
+# Copy from 
+# https://gitlab.cern.ch/lhcb/Moore/-/blob/2024-patches/Hlt/Hlt2Conf/python/Hlt2Conf/lines/charmonium_to_dimuon.py?ref_type=heads
+# https://gitlab.cern.ch/lhcb/Moore/-/blob/2024-patches/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bandq/builders/dimuon_lines.py?ref_type=heads
+import Functors as F
+
+from Hlt2Conf.standard_particles import make_long_muons
+from GaudiKernel.SystemOfUnits import MeV, GeV
+from RecoConf.reconstruction_objects import make_pvs
+from Hlt2Conf.algorithms_thor import ParticleFilter, ParticleCombiner
+
+
+_JPSI_PDG_MASS_ = 3096.9 * MeV
+_PSI2S_PDG_MASS_ = 3686.1 * MeV
+_MASSWINDOW_LOW_JPSI_ = 150 * MeV
+_MASSWINDOW_HIGH_JPSI_ = 150 * MeV
+_MASSWINDOW_LOW_PSI2S_ = 150 * MeV
+_MASSWINDOW_HIGH_PSI2S_ = 150 * MeV
+_MASSMIN_JPSI = _JPSI_PDG_MASS_ - _MASSWINDOW_LOW_JPSI_
+_MASSMAX_JPSI = _JPSI_PDG_MASS_ + _MASSWINDOW_HIGH_JPSI_
+_MASSMIN_PSI2S = _PSI2S_PDG_MASS_ - _MASSWINDOW_LOW_PSI2S_
+_MASSMAX_PSI2S = _PSI2S_PDG_MASS_ + _MASSWINDOW_HIGH_PSI2S_
+_MASSMIN_UPSILON = 7900 * MeV
+_MASSMAX_UPSILON = 12000 * MeV
+
+
+_PIDMU_JPSI = -999.
+_PIDMU_PSI2S = -999.
+_PIDMU_UPSILON = -999.
+
+def make_charmonium_muons(make_particles=make_long_muons,
+                          name='charmonium_muons_{hash}',
+                          minPt_muon=None,
+                          minP_muon=None,
+                          minIPChi2_muon=None,
+                          minIP_muon=None,
+                          minPIDmu=None,
+                          maxIPChi2_muon=None):
+
+    pvs = make_pvs()
+
+    code = F.require_all(F.ALL)
+
+    if minPt_muon is not None: code &= (F.PT > minPt_muon)
+    if minP_muon is not None: code &= (F.P > minP_muon)
+    if minIPChi2_muon is not None: code &= (F.MINIPCHI2(pvs) > minIPChi2_muon)
+    if minIP_muon is not None: code &= (F.MINIP(pvs) > minIP_muon)
+
+    # ignoring minPIDmu cut here, thus ignoring it in ALL lines in this file
+#    if not nopid_muons() and minPIDmu is not None:
+#        code &= F.require_all(F.PID_MU > minPIDmu)
+
+    if maxIPChi2_muon is not None:
+        code &= (F.MINIPCHI2(pvs) < maxIPChi2_muon)
+
+    return ParticleFilter(make_particles(), name=name, Cut=F.FILTER(code))
+
+
+
+def make_charmonium_dimuon_base(name='charmonium_dimuon_base_{hash}',
+                                DecayDescriptor='J/psi(1S) -> mu+ mu-',
+                                maxVertexChi2=25,
+                                maxDOCAChi2=None,
+                                minPt_muon=None,
+                                minP_muon=None,
+                                minIPChi2_muon=None,
+                                minIP_muon=None,
+                                minPIDmu=None,
+                                minPt_dimuon=None,
+                                minMass_dimuon=None,
+                                maxMass_dimuon=None):
+
+    # get the long muons
+    muons = make_charmonium_muons(
+        minPt_muon=minPt_muon,
+        minP_muon=minP_muon,
+        minIPChi2_muon=minIPChi2_muon,
+        minIP_muon=minIP_muon,
+        minPIDmu=minPIDmu)
+
+    combination_code = F.ALL
+    if minMass_dimuon is not None:
+        combination_code &= (F.MASS > minMass_dimuon)
+    if maxMass_dimuon is not None:
+        combination_code &= (F.MASS < maxMass_dimuon)
+    if maxDOCAChi2 is not None:
+        combination_code &= (F.SDOCACHI2(1, 2) < maxDOCAChi2)
+
+    # require that the muons come from the same vertex
+    vertex_code = F.require_all(F.CHI2DOF < maxVertexChi2)
+    if minPt_dimuon is not None: vertex_code &= (F.PT > minPt_dimuon)
+
+    return ParticleCombiner(
+        name=name,
+        Inputs=[muons, muons],
+        DecayDescriptor=DecayDescriptor,
+        CombinationCut=combination_code,
+        CompositeCut=vertex_code)
+
+
+def make_charmonium_dimuon(name='charmonium_dimuon_{hash}',
+                           DecayDescriptor='J/psi(1S) -> mu+ mu-',
+                           minPt_dimuon=None,
+                           minPt_muon=300 * MeV,
+                           minP_muon=None,
+                           minIPChi2_muon=None,
+                           maxVertexChi2=25,
+                           minPIDmu=None,
+                           bpvdls_min=None,
+                           maxDOCAChi2=None,
+                           minMass_dimuon=None,
+                           maxMass_dimuon=None):
+
+    make_particles = make_charmonium_dimuon_base(
+        DecayDescriptor=DecayDescriptor,
+        maxVertexChi2=maxVertexChi2,
+        minIPChi2_muon=minIPChi2_muon,
+        minPt_muon=minPt_muon,
+        minP_muon=minP_muon,
+        minPIDmu=minPIDmu,
+        maxDOCAChi2=maxDOCAChi2,
+        minMass_dimuon=minMass_dimuon,
+        maxMass_dimuon=maxMass_dimuon)
+
+    pvs = make_pvs()
+
+    code = F.require_all(F.CHI2DOF < maxVertexChi2)
+    if minPt_dimuon is not None: code &= (F.PT > minPt_dimuon)
+    if bpvdls_min is not None: code &= (F.BPVDLS(pvs) > bpvdls_min)
+
+    return ParticleFilter(make_particles, name=name, Cut=F.FILTER(code))
+
+
+
+
+def make_jpsi(name='jpsi_{hash}',
+              minMass_dimuon=_MASSMIN_JPSI,
+              maxMass_dimuon=_MASSMAX_JPSI,
+              minPt_muon=300 * MeV,
+              minP_muon=None,
+              minPt_Jpsi=None,
+              maxVertexChi2=25,
+              minPIDmu=_PIDMU_JPSI):
+
+    code = F.ALL
+    if minPt_Jpsi is not None: code &= F.PT > minPt_Jpsi
+
+    dimuon = make_charmonium_dimuon(
+        DecayDescriptor='J/psi(1S) -> mu+ mu-',
+        minPt_dimuon=minPt_Jpsi,
+        minPt_muon=minPt_muon,
+        minP_muon=minP_muon,
+        minPIDmu=minPIDmu,
+        minMass_dimuon=minMass_dimuon,
+        maxMass_dimuon=maxMass_dimuon,
+        maxVertexChi2=maxVertexChi2)
+
+    return ParticleFilter(dimuon, name=name, Cut=F.FILTER(code))
+
+
+def make_psi2s(name='psi2s_{hash}',
+               minMass_dimuon=_MASSMIN_PSI2S,
+               maxMass_dimuon=_MASSMAX_PSI2S,
+               minPt_muon=300 * MeV,
+               minP_muon=None,
+               minPt_Psi2S=None,
+               maxPt_Psi2S=None,
+               maxVertexChi2=25,
+               minPIDmu=_PIDMU_PSI2S):
+
+    code = F.ALL
+    if minPt_Psi2S is not None: code &= F.PT > minPt_Psi2S
+    if maxPt_Psi2S is not None: code &= F.PT < maxPt_Psi2S
+
+    dimuon = make_charmonium_dimuon(
+        DecayDescriptor='psi(2S) -> mu+ mu-',
+        minPt_dimuon=minPt_Psi2S,
+        minPt_muon=minPt_muon,
+        minP_muon=minP_muon,
+        minPIDmu=minPIDmu,
+        minMass_dimuon=minMass_dimuon,
+        maxMass_dimuon=maxMass_dimuon,
+        maxVertexChi2=maxVertexChi2)
+
+    return ParticleFilter(dimuon, name=name, Cut=F.FILTER(code))
+
+
+
+def make_upsilon(name='bandq_upsilon_{hash}',
+                 minMass_dimuon=_MASSMIN_UPSILON,
+                 maxMass_dimuon=_MASSMAX_UPSILON,
+                 minPt_muon=300 * MeV,
+                 minP_muon=0 * MeV,
+                 minPt_upsilon=0 * MeV):
+
+    code = (F.PT > minPt_upsilon)
+
+    dimuon = make_charmonium_dimuon(
+        DecayDescriptor='Upsilon(1S) -> mu+ mu-',
+        minPt_dimuon=minPt_upsilon,
+        minP_muon=minP_muon,
+        minPt_muon=minPt_muon,
+        minPIDmu=_PIDMU_UPSILON,
+        minMass_dimuon=minMass_dimuon,
+        maxMass_dimuon=maxMass_dimuon)
+
+    return ParticleFilter(dimuon, name=name, Cut=F.FILTER(code))
+
diff --git a/QuarkoniaToMuMu2024/info.yaml b/QuarkoniaToMuMu2024/info.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..41c5815222b56d10025c4bda7b5c9a9d8e0ed5a2
--- /dev/null
+++ b/QuarkoniaToMuMu2024/info.yaml
@@ -0,0 +1,49 @@
+defaults:
+    application: DaVinci/v64r12
+    wg: BandQ
+    inform:
+        - mengzhen.wang@cern.ch
+        - zehua.xu@cern.ch
+
+# mc configuration
+{%- set entrypoints = [
+  ("Jpsi", "Jpsi2mumu", "24142001", "Hlt2"),
+  ("Psi2S", "Psi2S2mumu", "28142001", "Hlt2"),
+  ("Upsilon1S", "Upsilon1S", "18112001", "Hlt2"),
+  ("Upsilon2S", "Upsilon2S", "18112011", "Hlt2"),
+  ("Upsilon3S", "Upsilon3S", "18112021", "Hlt2"),
+]%}
+
+{%- set MC_conditions = [
+  ("MagUp", "MagUp", "6.3", "6_3", "dddb-20240427", "sim10-2024.Q3.4-v1.3-mu100", "24Block1_v1", "2024", "2024.W31.34", "Sim10d"),
+]%}
+
+
+
+{%- for module, channel, eventtype, dv_input_process in entrypoints %}
+  {%- for polarity_mc, polarity_mc_jobtitle, nu, n_u, dddb, conddb, mcversion_jobtitle, BKK_label1, BKK_label2, simversion in MC_conditions %}
+
+MC_{{module}}_Nu{{n_u}}_{{polarity_mc_jobtitle}}_{{mcversion_jobtitle}}_ROOT_HLT1NoUT:
+    application: "DaVinci/v64r12@x86_64_v3-el9-gcc13+detdesc-opt+g"
+    input:
+        bk_query: "/MC/{{BKK_label1}}/Beam6800GeV-{{BKK_label2}}-{{ polarity_mc }}-Nu{{ nu }}-25ns-Pythia8/{{simversion}}/HLT1_2024.W31.34_noUT/HLT2-2024.W31.34/{{ eventtype }}/HLT2.DST"
+        keep_running: true 
+    output: QQbar2mumu_MC24_tuple.ROOT
+    options:
+        entrypoint: QuarkoniaToMuMu2024.dv_mc_{{channel}}:main
+        extra_options:
+            input_raw_format: 0.5
+            conddb_tag: {{ conddb }}
+            dddb_tag: {{ dddb }}
+            input_type: ROOT
+            simulation: True
+            data_type: "Upgrade"
+            input_process: "Hlt2"
+            input_stream: "bandq"
+
+
+  {%- endfor %}
+{%- endfor %}
+
+
+
diff --git a/QuarkoniaToMuMu2024/log b/QuarkoniaToMuMu2024/log
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391