diff --git a/Bc2KKpiMC/dv_data.py b/Bc2KKpiMC/dv_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..378be189164dc7e7285e4b7d2c9dd98b7bbed706
--- /dev/null
+++ b/Bc2KKpiMC/dv_data.py
@@ -0,0 +1,22 @@
+from .tupling_maker import template
+from DaVinci import Options, make_config
+from . import private_combiner
+
+from RecoConf.event_filters import require_pvs
+
+from RecoConf.reconstruction_objects import make_pvs
+def Bc2KKpi(options: Options):
+    decay_descriptor = {
+        "Bc": "[B_c+ -> K+ K- pi+ ]CC",
+        "Kp": "[B_c+ -> ^K+ K- pi+ ]CC",
+        "Km": "[B_c+ -> K+ ^K- pi+ ]CC",
+        "pip": "[B_c+ -> K+ K- ^pi+ ]CC",
+    }
+    descriptor='[B_c+ -> K+ K- pi+ ]cc'
+    kaon = private_combiner.make_kaon_forBc3h()
+    pion = private_combiner.make_pion_forBc3h()
+    particles = [kaon, kaon, pion]
+    line_name = 'SpruceBandQ_BcToKpKmPip'
+    my_tuple  = template(decay_descriptor, line_name, True,[],private_combiner.make_bc(particles,descriptor))
+#    my_filter = line_prefilter(line_name)
+    return make_config(options, [require_pvs(make_pvs()), my_tuple]) 
diff --git a/Bc2KKpiMC/info.yaml b/Bc2KKpiMC/info.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..79029dda12767b3a14270f4041e9a7bf038a9586
--- /dev/null
+++ b/Bc2KKpiMC/info.yaml
@@ -0,0 +1,37 @@
+defaults:
+    application: DaVinci/v64r12
+    wg: BandQ 
+    inform:
+        - tianyu.shi@cren.ch
+        - xuhao.yuan@cern.ch
+# data configuration
+{%- set data_info = [ 
+  ('14103034','W31_34', 'W31.34', 'HLT1_2024.W31.34_noUT/HLT2-2024.W31.34', 'MagUp',   'Nu6.3', 'dddb-20240427', 'sim10-2024.Q1.2-v1.1-mu100'),
+  ('14103034','W35_37', 'W35.37', 'HLT2-2024.W35.39', 'MagUp',   'Nu6.3', 'dddb-20240427', 'sim10-2024.Q1.2-v1.1-mu100'), 
+  ('14103034','W37_39', 'W37.39', 'HLT2-2024.W35.39', 'MagDown', 'Nu6.3', 'dddb-20240427', 'sim10-2024.Q1.2-v1.1-md100'),
+  ('14103034','W40_42', 'W40.42', 'HLT2-2024.W40.42', 'MagUp',   'Nu7.6', 'dddb-20240427', 'sim10-2024.Q1.2-v1.1-mu100'), 
+  ('14103034','W40_42', 'W40.42', 'HLT2-2024.W40.42', 'MagDown', 'Nu7.6', 'dddb-20240427', 'sim10-2024.Q1.2-v1.1-md100'),
+]%}
+{%- for event_type,block_name, block, hlt2, polarity, nu, dddb, conddb in data_info %}
+mc_Bc2KKpi_{{event_type}}_{{block_name}}_{{polarity}}_2024:
+    application: "DaVinci/v64r12"
+    input:
+      bk_query: "/MC/2024/Beam6800GeV-2024.{{block}}-{{polarity}}-{{nu}}-25ns-BcVegPyPythia8/Sim10e/{{hlt2}}/{{event_type}}/HLT2.DST"
+      dq_flags:
+        - OK
+      keep_running: true
+      n_test_lfns: 1
+    output: "mc_{{block_name}}.ROOT"
+    options:
+      entrypoint: Bc2KKpiMC.dv_data:Bc2KKpi
+      extra_options:
+        conddb_tag: {{ conddb }}
+        dddb_tag: {{ dddb }}
+        input_type: ROOT
+        input_raw_format: 0.5
+        simulation: True
+        data_type: 'Upgrade'
+        input_process: Hlt2
+        geometry_version: run3/2024.Q1.2-v00.00 
+        conditions_version: master
+{%- endfor %}
diff --git a/Bc2KKpiMC/private_combiner.py b/Bc2KKpiMC/private_combiner.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ebcf5a1fef5aa9f777fd8d3ebe9b3ab2d980385
--- /dev/null
+++ b/Bc2KKpiMC/private_combiner.py
@@ -0,0 +1,277 @@
+import Functors as F
+from Functors.math import in_range
+
+from GaudiKernel.SystemOfUnits import GeV, MeV, picosecond, mm
+
+from PyConf import configurable
+
+from Hlt2Conf.algorithms_thor import  ParticleCombiner, ParticleFilter
+from RecoConf.reconstruction_objects import make_pvs
+from Functors import require_all
+from PyConf import configurable
+from Hlt2Conf.standard_particles import make_has_rich_long_pions as make_pions, \
+    make_has_rich_long_kaons as make_kaons, \
+    make_has_rich_long_protons as make_protons
+from Hlt2Conf.lines.config_pid import nopid_hadrons
+
+_MASS_MIN = 120 * MeV  # minimal mass in case of pi0->gg
+
+def _make_generic(particles,
+                  descriptor,
+                  name='bandq_generic_{hash}',
+                  am_min=5100 * MeV,
+                  am_max=5550 * MeV,
+                  m_min=5140 * MeV,
+                  m_max=5510 * MeV,
+                  achi2_doca_max=25,
+                  vtx_chi2pdof_max=20,
+                  comb_cut_add=None,
+                  vtx_cut_add=None):
+
+    combination_code = (in_range(am_min, F.MASS, am_max))
+
+    vertex_code = require_all(
+        in_range(m_min, F.MASS, m_max), F.CHI2DOF < vtx_chi2pdof_max)
+
+    if comb_cut_add is not None:
+        combination_code &= comb_cut_add
+
+    if vtx_cut_add is not None:
+        vertex_code &= vtx_cut_add
+
+    if len(particles) == 2:
+        return ParticleCombiner(
+            name=name,
+            Inputs=particles,
+            DecayDescriptor=descriptor,
+            CombinationCut=combination_code,
+            CompositeCut=vertex_code)
+
+    if len(particles) == 3:
+
+        combination12_code = require_all(F.MASS < am_max - _MASS_MIN,
+                                         F.SDOCACHI2(1, 2) < achi2_doca_max)
+
+        combination_code &= require_all(
+            F.SDOCACHI2(1, 3) < achi2_doca_max,
+            F.SDOCACHI2(2, 3) < achi2_doca_max)
+
+        return ParticleCombiner(
+            name=name,
+            Inputs=particles,
+            DecayDescriptor=descriptor,
+            Combination12Cut=combination12_code,
+            CombinationCut=combination_code,
+            CompositeCut=vertex_code)
+
+    if len(particles) == 4:
+        combination12_code = require_all(F.MASS < am_max - 2 * _MASS_MIN,
+                                         F.SDOCACHI2(1, 2) < achi2_doca_max)
+
+        combination123_code = require_all(F.MASS < am_max - _MASS_MIN,
+                                          F.SDOCACHI2(1, 3) < achi2_doca_max,
+                                          F.SDOCACHI2(2, 3) < achi2_doca_max)
+
+        combination_code &= require_all(
+            F.SDOCACHI2(1, 4) < achi2_doca_max,
+            F.SDOCACHI2(2, 4) < achi2_doca_max,
+            F.SDOCACHI2(3, 4) < achi2_doca_max)
+
+        return ParticleCombiner(
+            name=name,
+            Inputs=particles,
+            DecayDescriptor=descriptor,
+            Combination12Cut=combination12_code,
+            Combination123Cut=combination123_code,
+            CombinationCut=combination_code,
+            CompositeCut=vertex_code)
+
+    if len(particles) >= 5:
+        combination12_code = require_all(F.MASS < am_max - 3 * _MASS_MIN,
+                                         F.SDOCACHI2(1, 2) < achi2_doca_max)
+
+        combination123_code = require_all(F.MASS < am_max - 2 * _MASS_MIN,
+                                          F.SDOCACHI2(1, 3) < achi2_doca_max,
+                                          F.SDOCACHI2(2, 3) < achi2_doca_max)
+
+        combination1234_code = require_all(F.MASS < am_max - _MASS_MIN,
+                                           F.SDOCACHI2(1, 4) < achi2_doca_max,
+                                           F.SDOCACHI2(2, 4) < achi2_doca_max,
+                                           F.SDOCACHI2(3, 4) < achi2_doca_max)
+
+        combination_code &= require_all(
+            F.SDOCACHI2(1, 5) < achi2_doca_max,
+            F.SDOCACHI2(2, 5) < achi2_doca_max,
+            F.SDOCACHI2(3, 5) < achi2_doca_max,
+            F.SDOCACHI2(4, 5) < achi2_doca_max)
+
+        return ParticleCombiner(
+            name=name,
+            Inputs=particles,
+            DecayDescriptor=descriptor,
+            Combination12Cut=combination12_code,
+            Combination123Cut=combination123_code,
+            Combination1234Cut=combination1234_code,
+            CombinationCut=combination_code,
+            CompositeCut=vertex_code)
+
+
+def make_b_hadron(
+        particles,
+        descriptor,
+        name='bandq_b_hadron_{hash}',
+        am_min=5100 * MeV,
+        am_max=5550 * MeV,
+        m_min=5140 * MeV,
+        m_max=5510 * MeV,
+        achi2_doca_max=25,
+        vtx_chi2pdof_max=20,
+        bpvltime_min=0.2 * picosecond,
+        bpvdira_min=0.995,  # [AVOID BIAS IN DIRECTION WRT TO PV], 0.995 (tan<0.1) is safe enough even for B from Tbb
+        bpvfdchi2_min=0,
+        minVDz=0. * mm,
+        minRho=0. * mm,
+        comb_cut_add=None):
+
+    vtx_cut_add = require_all(F.BPVLTIME() > bpvltime_min,
+                              F.BPVVDRHO() > minRho,
+                              F.BPVVDZ() > minVDz,
+                              F.BPVFDCHI2() > bpvfdchi2_min,
+                              F.BPVDIRA() > bpvdira_min)
+
+    return _make_generic(
+        particles=particles,
+        descriptor=descriptor,
+        name=name,
+        am_min=am_min,
+        am_max=am_max,
+        m_min=m_min,
+        m_max=m_max,
+        achi2_doca_max=achi2_doca_max,
+        vtx_chi2pdof_max=vtx_chi2pdof_max,
+        vtx_cut_add=vtx_cut_add,
+        comb_cut_add=comb_cut_add)
+
+
+def make_bc(particles,
+            descriptor,
+            name='bandq_Bc_{hash}',
+            am_min=6050 * MeV,
+            am_max=6555 * MeV,
+            m_min=6090 * MeV,
+            m_max=6510 * MeV,
+            bpvltime_min=0.1 * picosecond,
+            **decay_arguments):
+    """
+    Return B&Q Bc+.
+    """
+    return make_b_hadron(
+        particles,
+        descriptor,
+        name=name,
+        am_min=am_min,
+        am_max=am_max,
+        m_min=m_min,
+        m_max=m_max,
+        bpvltime_min=bpvltime_min,
+        **decay_arguments)
+
+def make_charged_hadrons(make_particles=make_pions,
+                         name="bandq_charged_hadrons_{hash}",
+                         pt_min=200. * MeV,
+                         p_min=2.5 * GeV,
+                         p_max=150. * GeV,
+                         eta_min=2.,
+                         eta_max=5.,
+                         mipchi2dvprimary_min=0,
+                         ghostProb_max=None,
+                         pid=None):
+
+    pvs = make_pvs()
+
+    code = require_all(F.PT > pt_min, in_range(p_min, F.P, p_max),
+                       in_range(eta_min, F.ETA, eta_max),
+                       F.MINIPCHI2(pvs) > mipchi2dvprimary_min)
+
+    if (pid is not None) and (not nopid_hadrons()):
+        code &= pid
+    if (ghostProb_max is not None):
+        code &= (F.GHOSTPROB < ghostProb_max)
+
+    return ParticleFilter(make_particles(), name=name, Cut=F.FILTER(code))
+
+def make_detached_pions(name='bandq_detached_pions_{hash}',
+                        mipchi2dvprimary_min=4.,
+                        pt_min=200. * MeV,
+                        p_min=2.5 * GeV,
+                        pid=(F.PID_K < 0.),
+                        ghostProb_max=None,
+                        **decay_arguments):
+    """
+    Return B&Q detached pions.
+    """
+    return make_charged_hadrons(
+        name=name,
+        make_particles=make_pions,
+        mipchi2dvprimary_min=mipchi2dvprimary_min,
+        pt_min=pt_min,
+        p_min=p_min,
+        pid=pid,
+        ghostProb_max=ghostProb_max,
+        **decay_arguments)
+def make_detached_kaons(name='bandq_detached_kaons_{hash}',
+                        mipchi2dvprimary_min=4.,
+                        pt_min=200. * MeV,
+                        p_min=2.5 * GeV,
+                        pid=(F.PID_K > 0.),
+                        ghostProb_max=None,
+                        **decay_arguments):
+    """
+    Return B&Q detached kaons.
+    """
+    return make_charged_hadrons(
+        make_particles=make_kaons,
+        name=name,
+        mipchi2dvprimary_min=mipchi2dvprimary_min,
+        pt_min=pt_min,
+        p_min=p_min,
+        pid=pid,
+        ghostProb_max=ghostProb_max,
+        **decay_arguments)
+
+def make_pion_forBc3h(name='bandq_pion_forBc3h_{hash}',
+                      pt_min=1000. * MeV,
+                      p_min=3.2 * GeV,
+                      pid=(F.PID_K < -5.),
+                      mipchi2dvprimary_min=12.):
+    return make_detached_pions(
+        name=name,
+        pt_min=pt_min,
+        p_min=p_min,
+        pid=pid,
+        mipchi2dvprimary_min=mipchi2dvprimary_min)
+
+def make_kaon_forBc3h(name='bandq_kaon_forBc3h_{hash}',
+                      pt_min=1000. * MeV,
+                      p_min=3.2 * GeV,
+                      pid=(F.PID_K > 5.),
+                      mipchi2dvprimary_min=12.):
+    return make_detached_kaons(
+        name=name,
+        pt_min=pt_min,
+        p_min=p_min,
+        pid=pid,
+        mipchi2dvprimary_min=mipchi2dvprimary_min)
+
+
+def make_BcToKpKmPip(name='bandq_BcToKpKmPip_{hash}'):
+    pion = make_pion_forBc3h()
+    kaon = make_kaon_forBc3h()
+    line_alg = make_bc(
+        name=name,
+        particles=[kaon, kaon, pion],
+        descriptor="[B_c+ -> K+ K- pi+ ]cc")
+    return line_alg
+
+
+
diff --git a/Bc2KKpiMC/tupling_maker.py b/Bc2KKpiMC/tupling_maker.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c6cb6f7ad998ca392afa0428c5a1fad5c7f8bae
--- /dev/null
+++ b/Bc2KKpiMC/tupling_maker.py
@@ -0,0 +1,349 @@
+###############################################################################
+# (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 . import private_combiner
+
+_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
+
+    all_vars += FC.Kinematics()# Add some usual variables: M, P, PTXYZ, E
+
+    if basic:
+        all_vars += FC.ParticleID(extra_info=True) # Add ProbNN and PID for basic partiels
+  
+    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({"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({"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({"MAXPT": F.MAX(F.PT)})
+        all_vars.update({"MAXDOCA": F.MAXSDOCA})
+        all_vars.update({"MAXDOCACHI2": F.MAXSDOCACHI2})
+#        all_vars.update({"SDOCA": F.SDOCA(1, 2)})
+#        all_vars.update({"SDOCACHI2": F.SDOCACHI2(1, 2)})
+#        all_vars.update({"SUBCOMB23_MM": F.SUBCOMB(Functor=F.MASS, Indices=(2, 3))})
+        all_vars.update({"SUMPT": F.SUM(F.PT)})
+        all_vars.update({"MINPT": F.MIN(F.PT)})
+        all_vars.update({"SUMPT": F.SUM(F.PT)})
+        all_vars.update({"MAXP": F.MAX(F.P)})
+        all_vars.update({"MINP": F.MIN(F.P)})
+        all_vars.update({"SUMP": F.SUM(F.P)})
+
+
+        
+
+    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({"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({"SHOWER_SHAPE": F.CALO_NEUTRAL_SHOWER_SHAPE})
+        all_vars.update({"TX": F.TX})
+        all_vars.update({"TY": F.TY})
+
+
+    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({"MINIP": F.MINIP(pvs)})
+    all_vars.update({"MINIPCHI2": F.MINIPCHI2(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})
+
+    
+    all_vars.update({"ETA": F.ETA})
+    all_vars.update({"FOURMOMENTUM": F.FOURMOMENTUM})
+#    all_vars.update({"ISBASIC": F.ISBASICPARTICLE})
+
+    all_vars.update({"MASS": F.MASS})
+    
+#    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})
+
+
+
+        
+    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 += 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, input_data):
+
+    evtpath_prefix = "/Event/Spruce/"
+    if isturbo:
+        evtpath_prefix = "/Event/HLT2/"
+
+#    input_data = get_particles(evtpath_prefix + f"{line_name}/Particles")
+
+    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
+    )
+
+
+    def extra_info(mctruth):
+        return FunctorCollection({
+            "TRUEKEY": F.VALUE_OR(-1) @ mctruth(F.OBJECT_KEY),
+            "TRUEEID": F.VALUE_OR(0) @ mctruth(F.PARTICLE_ID),
+            "TRUEORIGIN_VZ" : F.VALUE_OR(-1000) @ mctruth(F.ORIGIN_VZ),
+            "TRUEORIGIN_VX" : F.VALUE_OR(-1000) @ mctruth(F.ORIGIN_VX),
+            "TRUEORIGIN_VY" : F.VALUE_OR(-1000) @ mctruth(F.ORIGIN_VY),
+            "TRUEEND_VZ" : F.VALUE_OR(-1000) @ mctruth(F.END_VZ),
+            "TRUEEND_VX" : F.VALUE_OR(-1000) @ mctruth(F.END_VX),
+            "TRUEEND_VY" : F.VALUE_OR(-1000) @ mctruth(F.END_VY),
+            "TRUEENERGY": F.VALUE_OR(-1e6) @ mctruth(F.ENERGY),
+            "TRUEP":  F.VALUE_OR(-1e6) @ mctruth(F.P),
+            "TRUEFOURMOMENTUM": mctruth(F.FOURMOMENTUM),
+            "TRUE_PX" : F.VALUE_OR(-1e6) @ mctruth(F.PX),
+            "TRUE_PY" : F.VALUE_OR(-1e6) @ mctruth(F.PY),
+            "TRUE_PZ" : F.VALUE_OR(-1e6) @ mctruth(F.PZ),
+            "TRUE_MASS" : F.VALUE_OR(-1) @ mctruth(F.MASS),
+            "MC_LIFETIME" : F.VALUE_OR(-1) @ mctruth(F.MC_LIFETIME),
+            "BKGCAT": mctruth.BkgCat
+        })
+
+    mc_truth = MCTruthAndBkgCat(input_data, name = f"MCTruthAndBkgCat_{line_name}_" )
+
+    variables = {
+        "ALL"  : extra_info(mc_truth),
+        "Bc": composite_variables + all_variables(pvs, None, _composite),
+        "Kp": daughter_variables + all_variables(pvs, None, _basic),
+        "Km": daughter_variables + all_variables(pvs, None, _basic), 
+        "pip": daughter_variables + all_variables(pvs, None, _basic),
+    }
+
+    #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=input_data)
+
+    return my_tuple
+
+#def line_prefilter(line_name):
+#    return create_lines_filter(name=f"HLT_PASS{line_name}", lines=[line_name])