diff --git a/SpruceCalib_BdToDmPi/dv_simple.py b/SpruceCalib_BdToDmPi/dv_simple.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c014af406a3428818f6045102513d0b8704065f
--- /dev/null
+++ b/SpruceCalib_BdToDmPi/dv_simple.py
@@ -0,0 +1,217 @@
+# Based on:
+# https://gitlab.cern.ch/lhcb/DaVinci/-/blob/v63r2/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2.py
+
+import Functors as F
+import FunTuple.functorcollections as FC
+from FunTuple import  FunctorCollection, FunTuple_Particles as Funtuple
+from PyConf.reading import get_particles, get_pvs, get_rec_summary, get_odin
+from DaVinci.algorithms import create_lines_filter
+from DaVinci import Options, make_config
+from DaVinciMCTools import MCTruthAndBkgCat
+from DecayTreeFitter import DecayTreeFitter
+# specific for the B2OC SigmaNet
+import Functors.math as fmath
+import os
+
+def alg_config(options: Options):
+
+    line = "SpruceCalib_BdToDmPi_DmToPimPimKp"
+    line_data = get_particles(f"/Event/Spruce/{line}/Particles")
+    my_filter = create_lines_filter("Hlt2Line_Filter",
+                                    lines=[f"{line}"])
+    Hlt1_decisions = [
+        "Hlt1TrackMVADecision",
+        "Hlt1TwoTrackMVADecision",
+    ]
+    Hlt2_decisions = ['Hlt2Calib_BdToDmPi_DmToPimPimKp',
+                      'Hlt2Topo2BodyDecision',
+                      'Hlt2Topo3BodyDecision']
+
+    fields = {
+        "lab0": "[[B0]CC -> (D- -> K+ pi- pi-) pi+]CC",
+        "lab1": "[[B0]CC -> (D- -> K+ pi- pi-) ^pi+]CC",
+        "lab2": "[[B0]CC -> ^(D- -> K+ pi- pi-) pi+]CC",
+        "lab3": "[[B0]CC -> (D- -> ^K+ pi- pi-) pi+]CC",
+        "lab4": "[[B0]CC -> (D- -> K+ ^pi- pi-) pi+]CC",
+        "lab5": "[[B0]CC -> (D- -> K+ pi- ^pi-) pi+]CC",
+    }
+
+    pvs = get_pvs()
+
+    DTF_MassFitConsD = DecayTreeFitter(name="DTF_MassFitConsD",
+                                       input_particles=line_data,
+                                       mass_constraints=["D-"])
+
+    b_composite_variables = FunctorCollection(
+        {
+        "ID": F.PARTICLE_ID,
+        "PT": F.PT,
+        "ETA": F.ETA,
+        "P": F.P,
+        "SUMPT": F.SUM(F.PT),
+        "MASS": F.MASS,
+        "BPVDIRA": F.BPVDIRA(pvs),
+        "CHI2DOF": F.CHI2DOF,
+        "BPVIPCHI2": F.BPVIPCHI2(pvs),
+        "BPVIP": F.BPVIP(pvs),
+        "BPVFDCHI2": F.BPVFDCHI2(pvs),
+        "BPVLTIME": F.BPVLTIME(pvs),
+        "BPVFD": F.BPVFD(pvs),
+        "DTF_MassFitConsD_MASS": DTF_MassFitConsD(F.MASS),
+        "PX": F.PX,
+        "PY": F.PY,
+        "PZ": F.PZ,
+        "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_VCHI2DOF": F.CHI2DOF @ F.ENDVERTEX,
+        "BPVCHI2DOF": F.CHI2DOF @ F.BPV(pvs),
+        # B2OC generic B hadron NN Hlt2 algorithm,
+        # not planning to use it directly for B2OC EM
+        "MVA": F.MVA(
+            MVAType="SigmaNet",
+            Config={
+                "File":
+                "paramfile://data/Hlt2B2OC_B_SigmaNet_Run3-v2.json",
+                "Name":
+                "B2OC_SigmaNet_Generic",
+                "Lambda":
+                "2.0",
+                "NLayers":
+                "3",
+                "InputSize":
+                "6",
+                "Monotone_Constraints":
+                "[1,-1,-1,-1,-1,-1]",
+                "Variables":
+                "log_B_PT,B_ETA,log_B_DIRA,log_B_ENDVERTEX_CHI2,log_B_IPCHI2_OWNPV,log_B_IP_OWNPV",
+            },
+            Inputs={
+                "log_B_PT": fmath.log(F.PT),
+                "B_ETA": F.ETA,
+                "log_B_DIRA": fmath.log(1. +1.e-6 - F.BPVDIRA(pvs)),
+                "log_B_ENDVERTEX_CHI2": fmath.log(F.CHI2DOF),
+                "log_B_IPCHI2_OWNPV": fmath.log(F.BPVIPCHI2(pvs)),
+                "log_B_IP_OWNPV": fmath.log(F.BPVIP(pvs)),
+            }),
+        }
+    )
+    b_composite_variables+=FC.HltTisTos(selection_type="Hlt1", trigger_lines=Hlt1_decisions, data=line_data)
+
+    c_composite_variables = FunctorCollection(
+        {
+            "ID": F.PARTICLE_ID,
+            "PT": F.PT,
+            "ETA": F.ETA,
+            "P": F.P,
+            "SUMPT": F.SUM(F.PT),
+            "MASS": F.MASS,
+            "DOCA12": F.DOCA(1, 2),
+            "DOCA13": F.DOCA(1, 3),
+            "DOCA23": F.DOCA(2, 3),
+            "BPVDIRA": F.BPVDIRA(pvs),
+            "CHI2DOF": F.CHI2DOF,
+            "BPVIPCHI2": F.BPVIPCHI2(pvs),
+            "BPVFDCHI2": F.BPVFDCHI2(pvs),
+            "MINIPCHI2": F.MINIPCHI2(pvs),
+            "PX": F.PX,
+            "PY": F.PY,
+            "PZ": F.PZ,
+            "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_VCHI2DOF": F.CHI2DOF @ F.ENDVERTEX,
+        }
+    )
+
+    daughter_variables = FunctorCollection(
+        {
+            "ID": F.PARTICLE_ID,
+            "PT": F.PT,
+            "ETA": F.ETA,
+            "PHI": F.PHI,
+            "P": F.P,
+            "MASS": F.MASS,
+            "CHI2DOF": F.CHI2DOF,
+            "MINIPCHI2": F.MINIPCHI2(pvs), # for B2OC Hlt2 selections check
+            "BPVIPCHI2": F.BPVIPCHI2(pvs),
+            "PX": F.PX,
+            "PY": F.PY,
+            "PZ": F.PZ,
+            "PIDK": F.PID_K,
+            "PIDp": F.PID_P,
+            "PIDe": F.PID_E,
+            "PIDmu": F.PID_MU,
+            "isMuon": F.ISMUON,
+            "TRACK_GhostProb": F.GHOSTPROB,
+            "ProbNNp": F.PROBNN_P,
+            "NHITS": F.VALUE_OR(-1) @ F.NHITS @ F.TRACK,
+            "NVPHITS": F.VALUE_OR(-1) @ F.NVPHITS @ F.TRACK, # VeloPixel hits
+            "NUTHITS": F.VALUE_OR(-1) @ F.NUTHITS @ F.TRACK, # UpstreamTracker hits
+            "NFTHITS": F.VALUE_OR(-1) @ F.NFTHITS @ F.TRACK, # ForwardTracker hits
+            "TRACKHASVELO": F.VALUE_OR(-1) @ F.TRACKHASVELO @ F.TRACK,
+        }
+    )
+
+    variables = {
+        "lab0": b_composite_variables,
+        "lab1": daughter_variables,
+        "lab2": c_composite_variables,
+        "lab3": daughter_variables,
+        "lab4": daughter_variables,
+        "lab5": daughter_variables,
+    }
+
+    if options.simulation:
+        # get configured "MCTruthAndBkgCatAlg" algorithm for HLT2 output
+        mctruth = MCTruthAndBkgCat(line_data)
+        # add helper lambda that configures a functor to get truth information
+        MCTRUTH = lambda func: F.MAP_INPUT(Functor=func, Relations=mctruth.MCAssocTable)
+        trueid_bkgcat_info = {
+            # Important note: specify an invalid value for integer functors if there exists no truth info.
+            #                 The invalid value for floating point functors is set to nan.
+            "TRUEID": F.VALUE_OR(0) @ MCTRUTH(F.PARTICLE_ID),
+            "TRUEKEY": F.VALUE_OR(-1) @ MCTRUTH(F.OBJECT_KEY),
+            #
+            "TRUEPT": MCTRUTH(F.PT),
+            "TRUEPX": MCTRUTH(F.PX),
+            "TRUEPY": MCTRUTH(F.PY),
+            "TRUEPZ": MCTRUTH(F.PZ),
+            "TRUEENERGY": MCTRUTH(F.ENERGY),
+            "TRUEP": MCTRUTH(F.P),
+            "TRUEFOURMOMENTUM": MCTRUTH(F.FOURMOMENTUM),
+            "BKGCAT": F.BKGCAT(Relations=mctruth.BkgCatTable),
+        }
+        for field in variables.keys():
+            variables[field] += FunctorCollection(trueid_bkgcat_info)
+
+    odin = get_odin()
+    rec_summary = get_rec_summary()
+    # define event level variables
+    evt_variables = FunctorCollection({
+	"RUNNUMBER": F.RUNNUMBER(odin),
+	"EVENTNUMBER": F.EVENTNUMBER(odin),
+	"nPVs": F.VALUE_OR(-1) @ F.RECSUMMARY_INFO(rec_summary, "nPVs"),
+	"nLongTracks": F.VALUE_OR(-1) @ F.RECSUMMARY_INFO(rec_summary, "nLongTracks"),
+    })
+    evt_variables+=FC.SelectionInfo(selection_type="Hlt1", trigger_lines=Hlt1_decisions)
+    evt_variables+=FC.SelectionInfo(selection_type="Hlt2", trigger_lines=Hlt2_decisions)
+
+    # define FunTuple instance
+    my_tuple = Funtuple(
+        name="Tuple",
+        tuple_name="DecayTree",
+        fields=fields,
+        variables=variables,
+        event_variables=evt_variables,
+        inputs=line_data,
+        store_multiple_cand_info=True,
+    )
+
+    return make_config(options, [my_filter, my_tuple])
diff --git a/SpruceCalib_BdToDmPi/info-v1.yaml b/SpruceCalib_BdToDmPi/info-v1.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ec2af90481c60178eecb5c98197e838a42b32315
--- /dev/null
+++ b/SpruceCalib_BdToDmPi/info-v1.yaml
@@ -0,0 +1,44 @@
+
+checks:
+  hist_lab0_MASS:
+    type: range
+    expression: lab0_MASS
+    limits:
+      min: 5_000
+      max: 7_000
+    n_bins: 100
+
+defaults:
+  application: "DaVinci/v64r6"
+  output: DATA.ROOT
+  options:
+    entrypoint: SpruceCalib_BdToDmPi.dv_simple:alg_config
+    extra_options:
+      input_raw_format: 0.5
+      input_type: ROOT
+      simulation: False
+      data_type: "Upgrade"
+      geometry_version: run3/trunk
+      conditions_version: master
+      input_process: "Spruce"
+      input_stream: "b2oc"
+  inform:
+    - alessandro.bertolin@pd.infn.it
+  wg: B2OC
+
+{%- set datasets = [
+ ('2024Data', 'Down'),
+]%}
+{%- for evttype, polarity in datasets %}
+Bd2Dpi_{{evttype }}_{{ polarity }}:
+  input:
+    bk_query: "/LHCb/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}/Real Data/Sprucing24c2/90000000/B2OC.DST"
+    dq_flags:
+      - UNCHECKED
+      - OK
+    keep_running: true
+    n_test_lfns: 1
+  checks:
+   - hist_lab0_MASS
+{%- endfor %}
+
diff --git a/SpruceCalib_BdToDmPi/info.yaml b/SpruceCalib_BdToDmPi/info.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e4b9c35a001aac5e712f3747e904fec64d6e6dfb
--- /dev/null
+++ b/SpruceCalib_BdToDmPi/info.yaml
@@ -0,0 +1,33 @@
+
+defaults:
+  application: "DaVinci/v65r2"
+  output: DATA.ROOT
+  options:
+    entrypoint: SpruceCalib_BdToDmPi.dv_simple:alg_config
+    extra_options:
+      input_raw_format: 0.5
+      input_type: ROOT
+      simulation: False
+      data_type: "Upgrade"
+      geometry_version: run3/trunk
+      conditions_version: master
+      input_process: "Spruce"
+      input_stream: "b2oc"
+  inform:
+    - alessandro.bertolin@pd.infn.it
+  wg: B2OC
+
+{%- set datasets = [
+ ('2024Data', 'Down'),
+]%}
+{%- for evttype, polarity in datasets %}
+Bd2Dpi_{{evttype }}_{{ polarity }}:
+  input:
+    bk_query: "/LHCb/Collision24/Beam6800GeV-VeloClosed-Mag{{ polarity }}/Real Data/Sprucing24r1/90000000/B2OC.DST"
+    dq_flags:
+      - UNCHECKED
+      - OK
+    keep_running: true
+    n_test_lfns: 1
+{%- endfor %}
+