From 59d429dbcae3a4554269d5acc47a88738f1cd802 Mon Sep 17 00:00:00 2001
From: Jiahui Zhuo <jiahui.zhuo@cern.ch>
Date: Tue, 21 Jun 2022 09:58:05 +0200
Subject: [PATCH] Thor functors for MCTrackInfo

---
 .../tupling/example-tupling-mctrack-hlt.py    | 116 ++++++++
 .../tupling/example-tupling-mctrack-hlt.yaml  |  18 ++
 .../example-tupling-mctrack-mctuple-hlt.py    | 125 +++++++++
 .../example-tupling-mctrack-mctuple-hlt.yaml  |  18 ++
 .../test_davinci_tupling_mctrack_hlt.qmt      |  72 +++++
 ...st_davinci_tupling_mctrack_mctuple_hlt.qmt |  72 +++++
 Phys/DaVinci/python/DaVinci/mc_track.py       | 252 ++++++++++++++++++
 Phys/DaVinci/python/DaVinci/truth_matching.py |   6 +-
 8 files changed, 677 insertions(+), 2 deletions(-)
 create mode 100644 DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.py
 create mode 100644 DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.yaml
 create mode 100644 DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.py
 create mode 100644 DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.yaml
 create mode 100644 DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_hlt.qmt
 create mode 100644 DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_mctuple_hlt.qmt
 create mode 100644 Phys/DaVinci/python/DaVinci/mc_track.py

diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.py b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.py
new file mode 100644
index 000000000..1a2288053
--- /dev/null
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.py
@@ -0,0 +1,116 @@
+###############################################################################
+# (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.                                       #
+###############################################################################
+# Run this example in this diractory with:
+# ../../../../run davinci \
+# --inputfiledb test_hlt1_trigger_decisions ../../../../Phys/DaVinci/options/DaVinciDB-Example.yaml \
+# --joboptfile example-tupling-mctrack-hlt.yaml \
+# --user_algorithms example-tupling-mctrack-hlt:main
+
+from FunTuple import FunctorCollection
+import Functors as F
+from FunTuple import FunTuple_Particles as Funtuple
+from DaVinci import options
+from DaVinci.truth_matching import configured_MCTruthAndBkgCatAlg
+from DaVinci.algorithms import add_filter
+from PyConf.components import force_location
+
+from DaVinci.mc_track import MCP2MCTrackInfo
+
+
+def main():
+
+    # Input
+    B_data = force_location(
+        "/Event/HLT2/Hlt2B2OC_BdToDmPi_DmToPimPimKp_Line/Particles")
+
+    # Filter
+    filter_Dpi = add_filter(options, "HDRFilter",
+                            "HLT_PASS('Hlt2B2OC_BdToDmPi_DmToPimPimKp_Line')")
+
+    # Truth matching
+    mctruth = configured_MCTruthAndBkgCatAlg(
+        inputs=B_data,
+        filter_MCP=True,
+        # output_level=1,
+        root_in_tes="/Event/HLT2")
+    MC_TRUTH = lambda func: F.MAP_INPUT(Functor=func, Relations=mctruth.MCAssocTable)
+
+    # Tracks tools
+    MCP2MCTRACKINFO = MCP2MCTrackInfo(mctruth.TruthMatchedMCP, process='Hlt2')
+
+    # Variables
+    variables_all = FunctorCollection({
+        # REC
+        "P":
+        F.P,
+        "PT":
+        F.PT,
+        "PX":
+        F.PX,
+        "PY":
+        F.PY,
+        "PZ":
+        F.PZ,
+        "ENERGY":
+        F.ENERGY,
+        # TRUTH MATCHING
+        "TRUEP":
+        MC_TRUTH(F.P),
+        "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),
+        # TRUE TRACK
+        "MC_HAST":
+        MC_TRUTH(MCP2MCTRACKINFO.HasT),
+        "MC_HASTT":
+        MC_TRUTH(MCP2MCTRACKINFO.HasTT),
+        "MC_HASVELO":
+        MC_TRUTH(MCP2MCTRACKINFO.HasVelo),
+        "MC_ACCT":
+        MC_TRUTH(MCP2MCTRACKINFO.AccT),
+        "MC_ACCTT":
+        MC_TRUTH(MCP2MCTRACKINFO.AccTT),
+        "MC_ACCVELO":
+        MC_TRUTH(MCP2MCTRACKINFO.AccVelo),
+        # RECONSTRUCTIBLE CATEGORY
+        "MC_RECONSTRUCTIBLE":
+        MC_TRUTH(MCP2MCTRACKINFO.Reconstructible)
+    })
+
+    variables = {'ALL': variables_all}
+
+    # Define fields
+    fields = {
+        'B0': '[[B0]CC -> (D- -> K+ pi- pi-) pi+]CC',
+        'D': '[[B0]CC -> ^(D- -> K+ pi- pi-) pi+]CC',
+        'pi': '[[B0]CC -> (D- -> K+ pi- pi-) ^pi+]CC',
+        'Kplus': '[[B0]CC -> (D- -> ^K+ pi- pi-) pi+]CC',
+        'piminus_1': '[[B0]CC -> (D- -> K+ ^pi- pi-) pi+]CC',
+        'piminus_2': '[[B0]CC -> (D- -> K+ pi- ^pi-) pi+]CC'
+    }
+
+    # Make tuple algorithm
+    tuple_Dpi = Funtuple(
+        name="Dpi",
+        tuple_name="DecayTree",
+        fields=fields,
+        variables=variables,
+        inputs=B_data)
+
+    # Run
+    return {"KstG": [filter_Dpi, tuple_Dpi]}, []
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.yaml b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.yaml
new file mode 100644
index 000000000..724b96e8b
--- /dev/null
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.yaml
@@ -0,0 +1,18 @@
+###############################################################################
+# (c) Copyright 2020-2021 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.                                       #
+###############################################################################
+
+evt_max: 50
+annsvc_config: 'root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/hlt2_integration_B0_100.tck.json'
+histo_file: 'DV_histo_example-tupling-mctrack-hlt.root'
+ntuple_file: 'DV_tuple_example-tupling-mctrack-hlt.root'
+lumi: false
+print_freq: 1
+process: 'Hlt2'
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.py b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.py
new file mode 100644
index 000000000..a4d1cd909
--- /dev/null
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.py
@@ -0,0 +1,125 @@
+###############################################################################
+# (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.                                       #
+###############################################################################
+# Run this example in this diractory with:
+# ../../../../run davinci \
+# --inputfiledb test_hlt1_trigger_decisions ../../../../Phys/DaVinci/options/DaVinciDB-Example.yaml \
+# --joboptfile example-tupling-mctrack-hlt.yaml \
+# --user_algorithms example-tupling-mctrack-hlt:main
+
+from FunTuple import FunctorCollection
+import Functors as F
+from FunTuple import FunTuple_MCParticles as MCFuntuple
+from DaVinci import options
+from DaVinci.algorithms import add_filter
+from PyConf.components import force_location
+
+from DaVinci.mc_track import MCP2MCTrackInfo, MCP2Track
+
+
+def main():
+
+    # Input
+    MC_data = force_location("/Event/HLT2/MC/Particles")
+
+    # Filter
+    filter_Dpi = add_filter(options, "HDRFilter",
+                            "HLT_PASS('Hlt2B2OC_BdToDmPi_DmToPimPimKp_Line')")
+
+    # Tracks tools
+    MCP2MCTRACKINFO = MCP2MCTrackInfo(MC_data, process='Hlt2')
+    MCP2TRACK = MCP2Track(MC_data, process='Hlt2')
+
+    # Variables
+    variables_all = FunctorCollection({
+        # REC
+        "P":
+        F.P,
+        "PT":
+        F.PT,
+        "PX":
+        F.PX,
+        "PY":
+        F.PY,
+        "PZ":
+        F.PZ,
+        "ENERGY":
+        F.ENERGY,
+        # RECONSTRUCTED TRACK
+        "MC_RecoTrack_TYPE":
+        MCP2TRACK.Type,
+        "MC_RecoTrack_REF_X":
+        MCP2TRACK.ReferencePoint_X,
+        "MC_RecoTrack_REF_Y":
+        MCP2TRACK.ReferencePoint_Y,
+        "MC_RecoTrack_REF_Z":
+        MCP2TRACK.ReferencePoint_Z,
+        "MC_RecoTrack_NDOF":
+        MCP2TRACK.nDoF,
+        "MC_RecoTrack_GHOSTPROB":
+        MCP2TRACK.GhostProbability,
+        "MC_RecoTrack_FLAG":
+        MCP2TRACK.Flag,
+        "MC_RecoTrack_HAST":
+        MCP2TRACK.HasT,
+        "MC_RecoTrack_HASUT":
+        MCP2TRACK.HasUT,
+        "MC_RecoTrack_HASVELO":
+        MCP2TRACK.HasVelo,
+        "MC_RecoTrack_NHITS":
+        MCP2TRACK.nHits,
+        "MC_RecoTrack_NVPHITS":
+        MCP2TRACK.nVPHits,
+        "MC_RecoTrack_NUTHITS":
+        MCP2TRACK.nUTHits,
+        "MC_RecoTrack_NFTHITS":
+        MCP2TRACK.nFTHits,
+        # TRUE TRACK
+        "MC_HAST":
+        MCP2MCTRACKINFO.HasT,
+        "MC_HASTT":
+        MCP2MCTRACKINFO.HasTT,
+        "MC_HASVELO":
+        MCP2MCTRACKINFO.HasVelo,
+        "MC_ACCT":
+        MCP2MCTRACKINFO.AccT,
+        "MC_ACCTT":
+        MCP2MCTRACKINFO.AccTT,
+        "MC_ACCVELO":
+        MCP2MCTRACKINFO.AccVelo,
+        # RECONSTRUCTED CATEGORY AND RECONSTRUCTIBLE CATEGORY
+        "MC_RECONSTRUCTED":
+        MCP2TRACK.Reconstructed,
+        "MC_RECONSTRUCTIBLE":
+        MCP2MCTRACKINFO.Reconstructible,
+    })
+
+    variables = {'ALL': variables_all}
+
+    # Define fields
+    fields = {
+        'B0': '[[B0]CC -> (D- -> K+ pi- pi-) pi+]CC',
+        'D': '[[B0]CC -> ^(D- -> K+ pi- pi-) pi+]CC',
+        'pi': '[[B0]CC -> (D- -> K+ pi- pi-) ^pi+]CC',
+        'Kplus': '[[B0]CC -> (D- -> ^K+ pi- pi-) pi+]CC',
+        'piminus_1': '[[B0]CC -> (D- -> K+ ^pi- pi-) pi+]CC',
+        'piminus_2': '[[B0]CC -> (D- -> K+ pi- ^pi-) pi+]CC'
+    }
+
+    # Make tuple algorithm
+    tuple_Dpi = MCFuntuple(
+        name="DpiMC",
+        tuple_name="DecayTree",
+        fields=fields,
+        variables=variables,
+        inputs=MC_data)
+
+    # Run
+    return {"KstG": [filter_Dpi, tuple_Dpi]}, []
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.yaml b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.yaml
new file mode 100644
index 000000000..70e503a65
--- /dev/null
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.yaml
@@ -0,0 +1,18 @@
+###############################################################################
+# (c) Copyright 2020-2021 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.                                       #
+###############################################################################
+
+evt_max: 50
+annsvc_config: 'root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/hlt2_integration_B0_100.tck.json'
+histo_file: 'DV_histo_example-tupling-mctrack-mctuple-hlt.root'
+ntuple_file: 'DV_tuple_example-tupling-mctrack-mctuple-hlt.root'
+lumi: false
+print_freq: 1
+process: 'Hlt2'
diff --git a/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_hlt.qmt b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_hlt.qmt
new file mode 100644
index 000000000..2adc0e0ec
--- /dev/null
+++ b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_hlt.qmt
@@ -0,0 +1,72 @@
+<?xml version="1.0" ?>
+<!--
+###############################################################################
+# (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.                                       #
+###############################################################################
+-->
+<!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+#######################################################
+# SUMMARY OF THIS TEST
+# ...................
+# Author: Jiahui Zhuo
+# Purpose: Test for MCTrack related functor in HLT2 process
+# Prerequisites: None
+# inputfiledb test_hlt1_trigger_decisions $DAVINCIROOT/options/DaVinciDB-Example.yaml
+# user_algorithms ../../python/DaVinciExamples/tupling/example-tupling-mctrack-hlt:main
+#######################################################
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+  <argument name="program"><text>davinci</text></argument>
+  <argument name="args"><set>
+  <text>--inputfiledb</text>
+  <text>test_hlt1_trigger_decisions</text>
+  <text>$DAVINCIROOT/options/DaVinciDB-Example.yaml</text>
+  <text>--joboptfile</text>
+  <text>../../python/DaVinciExamples/tupling/example-tupling-mctrack-hlt.yaml</text>
+  <text>--user_algorithms</text>
+  <text>../../python/DaVinciExamples/tupling/example-tupling-mctrack-hlt:main</text>
+  </set></argument>
+  <argument name="validator"><text>
+findReferenceBlock("""
+RFileCnv                               INFO dumping contents of /NTUPLES/FILE1
+TFile: name=DV_tuple_example-tupling-mctrack-hlt.root, title=Gaudi Trees, option=CREATE
+******************************************************************************
+*Tree    :DecayTree : DecayTree                                              *
+""", stdout, result, causes, signature_offset = 0)
+
+import sys, os
+from ROOT import TFile
+
+B_vars_stored =['B0_ENERGY', 'B0_MC_ACCT', 'B0_MC_ACCTT', 'B0_MC_ACCVELO', 'B0_MC_HAST', 'B0_MC_HASTT', 'B0_MC_HASVELO', 'B0_MC_RECONSTRUCTIBLE', 'B0_P', 'B0_PT', 'B0_PX', 'B0_PY', 'B0_PZ', 'B0_TRUEENERGY', 'B0_TRUEP', 'B0_TRUEPT', 'B0_TRUEPX', 'B0_TRUEPY', 'B0_TRUEPZ', 'D_ENERGY', 'D_MC_ACCT', 'D_MC_ACCTT', 'D_MC_ACCVELO', 'D_MC_HAST', 'D_MC_HASTT', 'D_MC_HASVELO', 'D_MC_RECONSTRUCTIBLE', 'D_P', 'D_PT', 'D_PX', 'D_PY', 'D_PZ', 'D_TRUEENERGY', 'D_TRUEP', 'D_TRUEPT', 'D_TRUEPX', 'D_TRUEPY', 'D_TRUEPZ', 'Kplus_ENERGY', 'Kplus_MC_ACCT', 'Kplus_MC_ACCTT', 'Kplus_MC_ACCVELO', 'Kplus_MC_HAST', 'Kplus_MC_HASTT', 'Kplus_MC_HASVELO', 'Kplus_MC_RECONSTRUCTIBLE', 'Kplus_P', 'Kplus_PT', 'Kplus_PX', 'Kplus_PY', 'Kplus_PZ', 'Kplus_TRUEENERGY', 'Kplus_TRUEP', 'Kplus_TRUEPT', 'Kplus_TRUEPX', 'Kplus_TRUEPY', 'Kplus_TRUEPZ', 'pi_ENERGY', 'pi_MC_ACCT', 'pi_MC_ACCTT', 'pi_MC_ACCVELO', 'pi_MC_HAST', 'pi_MC_HASTT', 'pi_MC_HASVELO', 'pi_MC_RECONSTRUCTIBLE', 'pi_P', 'pi_PT', 'pi_PX', 'pi_PY', 'pi_PZ', 'pi_TRUEENERGY', 'pi_TRUEP', 'pi_TRUEPT', 'pi_TRUEPX', 'pi_TRUEPY', 'pi_TRUEPZ', 'piminus_1_ENERGY', 'piminus_1_MC_ACCT', 'piminus_1_MC_ACCTT', 'piminus_1_MC_ACCVELO', 'piminus_1_MC_HAST', 'piminus_1_MC_HASTT', 'piminus_1_MC_HASVELO', 'piminus_1_MC_RECONSTRUCTIBLE', 'piminus_1_P', 'piminus_1_PT', 'piminus_1_PX', 'piminus_1_PY', 'piminus_1_PZ', 'piminus_1_TRUEENERGY', 'piminus_1_TRUEP', 'piminus_1_TRUEPT', 'piminus_1_TRUEPX', 'piminus_1_TRUEPY', 'piminus_1_TRUEPZ', 'piminus_2_ENERGY', 'piminus_2_MC_ACCT', 'piminus_2_MC_ACCTT', 'piminus_2_MC_ACCVELO', 'piminus_2_MC_HAST', 'piminus_2_MC_HASTT', 'piminus_2_MC_HASVELO', 'piminus_2_MC_RECONSTRUCTIBLE', 'piminus_2_P', 'piminus_2_PT', 'piminus_2_PX', 'piminus_2_PY', 'piminus_2_PZ', 'piminus_2_TRUEENERGY', 'piminus_2_TRUEP', 'piminus_2_TRUEPT', 'piminus_2_TRUEPX', 'piminus_2_TRUEPY', 'piminus_2_TRUEPZ']
+
+#sort the expected vars
+B_vars_stored = sorted(B_vars_stored)
+
+#open the TFile and TTree
+ntuple = './DV_tuple_example-tupling-mctrack-hlt.root'
+if not os.path.isfile(ntuple): raise Exception(f"File: {ntuple} does not exist!")
+f      = TFile.Open(ntuple)
+t_B    = f.Get('Dpi/DecayTree')
+
+#sort the stores vars
+b_names = sorted([b.GetName() for b in t_B.GetListOfLeaves()])
+
+B_excluded_1 = set(B_vars_stored) - set(b_names)
+B_excluded_2 = set(b_names) - set(B_vars_stored)
+if len(B_excluded_1) != 0: raise Exception('Number of stored variables is less than what is expected. The extra variables expected are: ' , B_excluded_1)
+if len(B_excluded_2) != 0: raise Exception('Number of stored variables is greater than what is expected. The extra variables stored are: ', B_excluded_2)
+
+f.Close()
+print('Test successfully completed!')
+os.system(f"rm {ntuple}")
+countErrorLines({"FATAL":0, "ERROR":0})
+  </text></argument>
+</extension>
diff --git a/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_mctuple_hlt.qmt b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_mctuple_hlt.qmt
new file mode 100644
index 000000000..15b584e42
--- /dev/null
+++ b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_mctrack_mctuple_hlt.qmt
@@ -0,0 +1,72 @@
+<?xml version="1.0" ?>
+<!--
+###############################################################################
+# (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.                                       #
+###############################################################################
+-->
+<!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+#######################################################
+# SUMMARY OF THIS TEST
+# ...................
+# Author: Jiahui Zhuo
+# Purpose: Test for MCTrack related functor in HLT2 process and FunTuple_MCParticles
+# Prerequisites: None
+# inputfiledb test_hlt1_trigger_decisions $DAVINCIROOT/options/DaVinciDB-Example.yaml
+# user_algorithms ../../python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt:main
+#######################################################
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+  <argument name="program"><text>davinci</text></argument>
+  <argument name="args"><set>
+  <text>--inputfiledb</text>
+  <text>test_hlt1_trigger_decisions</text>
+  <text>$DAVINCIROOT/options/DaVinciDB-Example.yaml</text>
+  <text>--joboptfile</text>
+  <text>../../python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt.yaml</text>
+  <text>--user_algorithms</text>
+  <text>../../python/DaVinciExamples/tupling/example-tupling-mctrack-mctuple-hlt:main</text>
+  </set></argument>
+  <argument name="validator"><text>
+findReferenceBlock("""
+RFileCnv                               INFO dumping contents of /NTUPLES/FILE1
+TFile: name=DV_tuple_example-tupling-mctrack-mctuple-hlt.root, title=Gaudi Trees, option=CREATE
+******************************************************************************
+*Tree    :DecayTree : DecayTree                                              *
+""", stdout, result, causes, signature_offset = 0)
+
+import sys, os
+from ROOT import TFile
+
+B_vars_stored =['B0_ENERGY', 'B0_MC_ACCT', 'B0_MC_ACCTT', 'B0_MC_ACCVELO', 'B0_MC_HAST', 'B0_MC_HASTT', 'B0_MC_HASVELO', 'B0_MC_RECONSTRUCTED', 'B0_MC_RECONSTRUCTIBLE', 'B0_MC_RecoTrack_FLAG', 'B0_MC_RecoTrack_GHOSTPROB', 'B0_MC_RecoTrack_HAST', 'B0_MC_RecoTrack_HASUT', 'B0_MC_RecoTrack_HASVELO', 'B0_MC_RecoTrack_NDOF', 'B0_MC_RecoTrack_NFTHITS', 'B0_MC_RecoTrack_NHITS', 'B0_MC_RecoTrack_NUTHITS', 'B0_MC_RecoTrack_NVPHITS', 'B0_MC_RecoTrack_REF_X', 'B0_MC_RecoTrack_REF_Y', 'B0_MC_RecoTrack_REF_Z', 'B0_MC_RecoTrack_TYPE', 'B0_P', 'B0_PT', 'B0_PX', 'B0_PY', 'B0_PZ', 'D_ENERGY', 'D_MC_ACCT', 'D_MC_ACCTT', 'D_MC_ACCVELO', 'D_MC_HAST', 'D_MC_HASTT', 'D_MC_HASVELO', 'D_MC_RECONSTRUCTED', 'D_MC_RECONSTRUCTIBLE', 'D_MC_RecoTrack_FLAG', 'D_MC_RecoTrack_GHOSTPROB', 'D_MC_RecoTrack_HAST', 'D_MC_RecoTrack_HASUT', 'D_MC_RecoTrack_HASVELO', 'D_MC_RecoTrack_NDOF', 'D_MC_RecoTrack_NFTHITS', 'D_MC_RecoTrack_NHITS', 'D_MC_RecoTrack_NUTHITS', 'D_MC_RecoTrack_NVPHITS', 'D_MC_RecoTrack_REF_X', 'D_MC_RecoTrack_REF_Y', 'D_MC_RecoTrack_REF_Z', 'D_MC_RecoTrack_TYPE', 'D_P', 'D_PT', 'D_PX', 'D_PY', 'D_PZ', 'Kplus_ENERGY', 'Kplus_MC_ACCT', 'Kplus_MC_ACCTT', 'Kplus_MC_ACCVELO', 'Kplus_MC_HAST', 'Kplus_MC_HASTT', 'Kplus_MC_HASVELO', 'Kplus_MC_RECONSTRUCTED', 'Kplus_MC_RECONSTRUCTIBLE', 'Kplus_MC_RecoTrack_FLAG', 'Kplus_MC_RecoTrack_GHOSTPROB', 'Kplus_MC_RecoTrack_HAST', 'Kplus_MC_RecoTrack_HASUT', 'Kplus_MC_RecoTrack_HASVELO', 'Kplus_MC_RecoTrack_NDOF', 'Kplus_MC_RecoTrack_NFTHITS', 'Kplus_MC_RecoTrack_NHITS', 'Kplus_MC_RecoTrack_NUTHITS', 'Kplus_MC_RecoTrack_NVPHITS', 'Kplus_MC_RecoTrack_REF_X', 'Kplus_MC_RecoTrack_REF_Y', 'Kplus_MC_RecoTrack_REF_Z', 'Kplus_MC_RecoTrack_TYPE', 'Kplus_P', 'Kplus_PT', 'Kplus_PX', 'Kplus_PY', 'Kplus_PZ', 'pi_ENERGY', 'pi_MC_ACCT', 'pi_MC_ACCTT', 'pi_MC_ACCVELO', 'pi_MC_HAST', 'pi_MC_HASTT', 'pi_MC_HASVELO', 'pi_MC_RECONSTRUCTED', 'pi_MC_RECONSTRUCTIBLE', 'pi_MC_RecoTrack_FLAG', 'pi_MC_RecoTrack_GHOSTPROB', 'pi_MC_RecoTrack_HAST', 'pi_MC_RecoTrack_HASUT', 'pi_MC_RecoTrack_HASVELO', 'pi_MC_RecoTrack_NDOF', 'pi_MC_RecoTrack_NFTHITS', 'pi_MC_RecoTrack_NHITS', 'pi_MC_RecoTrack_NUTHITS', 'pi_MC_RecoTrack_NVPHITS', 'pi_MC_RecoTrack_REF_X', 'pi_MC_RecoTrack_REF_Y', 'pi_MC_RecoTrack_REF_Z', 'pi_MC_RecoTrack_TYPE', 'pi_P', 'pi_PT', 'pi_PX', 'pi_PY', 'pi_PZ', 'piminus_1_ENERGY', 'piminus_1_MC_ACCT', 'piminus_1_MC_ACCTT', 'piminus_1_MC_ACCVELO', 'piminus_1_MC_HAST', 'piminus_1_MC_HASTT', 'piminus_1_MC_HASVELO', 'piminus_1_MC_RECONSTRUCTED', 'piminus_1_MC_RECONSTRUCTIBLE', 'piminus_1_MC_RecoTrack_FLAG', 'piminus_1_MC_RecoTrack_GHOSTPROB', 'piminus_1_MC_RecoTrack_HAST', 'piminus_1_MC_RecoTrack_HASUT', 'piminus_1_MC_RecoTrack_HASVELO', 'piminus_1_MC_RecoTrack_NDOF', 'piminus_1_MC_RecoTrack_NFTHITS', 'piminus_1_MC_RecoTrack_NHITS', 'piminus_1_MC_RecoTrack_NUTHITS', 'piminus_1_MC_RecoTrack_NVPHITS', 'piminus_1_MC_RecoTrack_REF_X', 'piminus_1_MC_RecoTrack_REF_Y', 'piminus_1_MC_RecoTrack_REF_Z', 'piminus_1_MC_RecoTrack_TYPE', 'piminus_1_P', 'piminus_1_PT', 'piminus_1_PX', 'piminus_1_PY', 'piminus_1_PZ', 'piminus_2_ENERGY', 'piminus_2_MC_ACCT', 'piminus_2_MC_ACCTT', 'piminus_2_MC_ACCVELO', 'piminus_2_MC_HAST', 'piminus_2_MC_HASTT', 'piminus_2_MC_HASVELO', 'piminus_2_MC_RECONSTRUCTED', 'piminus_2_MC_RECONSTRUCTIBLE', 'piminus_2_MC_RecoTrack_FLAG', 'piminus_2_MC_RecoTrack_GHOSTPROB', 'piminus_2_MC_RecoTrack_HAST', 'piminus_2_MC_RecoTrack_HASUT', 'piminus_2_MC_RecoTrack_HASVELO', 'piminus_2_MC_RecoTrack_NDOF', 'piminus_2_MC_RecoTrack_NFTHITS', 'piminus_2_MC_RecoTrack_NHITS', 'piminus_2_MC_RecoTrack_NUTHITS', 'piminus_2_MC_RecoTrack_NVPHITS', 'piminus_2_MC_RecoTrack_REF_X', 'piminus_2_MC_RecoTrack_REF_Y', 'piminus_2_MC_RecoTrack_REF_Z', 'piminus_2_MC_RecoTrack_TYPE', 'piminus_2_P', 'piminus_2_PT', 'piminus_2_PX', 'piminus_2_PY', 'piminus_2_PZ']
+
+#sort the expected vars
+B_vars_stored = sorted(B_vars_stored)
+
+#open the TFile and TTree
+ntuple = './DV_tuple_example-tupling-mctrack-mctuple-hlt.root'
+if not os.path.isfile(ntuple): raise Exception(f"File: {ntuple} does not exist!")
+f      = TFile.Open(ntuple)
+t_B    = f.Get('DpiMC/DecayTree')
+
+#sort the stores vars
+b_names = sorted([b.GetName() for b in t_B.GetListOfLeaves()])
+
+B_excluded_1 = set(B_vars_stored) - set(b_names)
+B_excluded_2 = set(b_names) - set(B_vars_stored)
+if len(B_excluded_1) != 0: raise Exception('Number of stored variables is less than what is expected. The extra variables expected are: ' , B_excluded_1)
+if len(B_excluded_2) != 0: raise Exception('Number of stored variables is greater than what is expected. The extra variables stored are: ', B_excluded_2)
+
+f.Close()
+print('Test successfully completed!')
+os.system(f"rm {ntuple}")
+countErrorLines({"FATAL":0, "ERROR":0})
+  </text></argument>
+</extension>
diff --git a/Phys/DaVinci/python/DaVinci/mc_track.py b/Phys/DaVinci/python/DaVinci/mc_track.py
new file mode 100644
index 000000000..af1cc3abc
--- /dev/null
+++ b/Phys/DaVinci/python/DaVinci/mc_track.py
@@ -0,0 +1,252 @@
+###############################################################################
+# (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.                                       #
+###############################################################################
+
+from Gaudi.Configuration import INFO
+from Functors.grammar import BoundFunctor
+from DaVinci.data_from_file import make_mc_track_info
+from PyConf.components import force_location
+from PyConf.Algorithms import MC2MCTrackInfoAlg, MC2TrackAlg
+import Functors as F
+
+
+class MCP2MCTrackInfo:
+    """
+    MCP2MCTrackInfo helper class, will create all possible True TrackInfo related functors and can be
+    accessed as the standard python class member.
+
+    Args:
+        mcparticles_input : MCParticle data handle (TES)
+        mc_track_info (optional) : MCTrackInfo handle (TES), default = make_mc_track_info()
+        process       (optional) : Process type (Hlt2 or Spruce), default = 'Hlt2'
+        root_in_tes   (optional) : Standard Gaudi Algorithm RootInTES location
+        output_level  (optional) : Standard Gaudi Algorithm OutputLevel
+
+    Example:
+        MC_TRINFO = MC_TrackInfo(MCParticles)
+        allvariables['HAST'] = MC_TRINFO.HasT 
+
+    """
+
+    # Copied definition from /LHCb/Event/MCEvent/include/Event/MCTrackInfo.h
+    class _Flag:
+        VeloR = 0x00000001
+        VeloPhi = 0x00000002
+        TT1 = 0x00000004
+        TT2 = 0x00000008
+        T1X = 0x00000010
+        T1S = 0x00000020
+        T2X = 0x00000040
+        T2S = 0x00000080
+        T3X = 0x00000100
+        T3S = 0x00000200
+        HasVelo = VeloR | VeloPhi
+        HasTT = TT1 | TT2
+        HasT1 = T1X | T1S
+        HasT2 = T2X | T2S
+        HasT3 = T3X | T3S
+        HasT = HasT1 | HasT2 | HasT3
+        HasVeloAndT = HasVelo | HasT
+        AccVeloR = 0x00000400
+        AccVeloPhi = 0x00000800
+        AccTT1 = 0x00001000
+        AccTT2 = 0x00002000
+        AccT1X = 0x00004000
+        AccT1S = 0x00008000
+        AccT2X = 0x00010000
+        AccT2S = 0x00020000
+        AccT3X = 0x00040000
+        AccT3S = 0x00080000
+        AccVelo = AccVeloR | AccVeloPhi
+        AccTT = AccTT1 | AccTT2
+        AccT1 = AccT1X | AccT1S
+        AccT2 = AccT2X | AccT2S
+        AccT3 = AccT3X | AccT3S
+        AccT = AccT1 | AccT2 | AccT3
+        AccVeloAndT = AccVelo | AccT
+        multVeloR = 20
+        multVeloPhi = 25
+        MultVeloR = 0x01F00000
+        MultVeloPhi = 0x3E000000
+
+    def get_info(self, mc_property: int):
+        return F.MAP_INPUT(
+            Functor=F.MC_TRACKINFO(mc_property), Relations=self.MC2MCTrackInfo)
+
+    def __init__(self,
+                 mcparticles_input,
+                 mc_track_info=make_mc_track_info(),
+                 process='Spruce',
+                 output_level=INFO):
+
+        if process in ['Spruce', 'Turbo']:
+            raise ValueError(
+                "MCTrackInfo is not persisted in Spruce/Turbo at the moment, this will be fixed in the future, see: "
+                "https://gitlab.cern.ch/lhcb/Moore/-/issues/440")
+        elif process != 'Hlt2':
+            raise ValueError(
+                f"The specified 'process' {process} not recognised. Can only be 'Hlt2' or 'Spruce' or 'Turbo'."
+            )
+
+        # Load Algorithm
+        self.Algorithm = MC2MCTrackInfoAlg(
+            Input=mcparticles_input,
+            MCTrackInfo=mc_track_info,
+            OutputLevel=output_level)
+
+        # Alias output of algorithm
+        self.MC2MCTrackInfo = self.Algorithm.MC2MCTrackInfo
+        self.MC2Reconstructible = self.Algorithm.MC2Reconstructible
+
+        # Create functors for MC_TRACKINFO
+        self.VeloR = self.get_info(self._Flag.VeloR)
+        self.VeloPhi = self.get_info(self._Flag.VeloPhi)
+        self.TT1 = self.get_info(self._Flag.TT1)
+        self.TT2 = self.get_info(self._Flag.TT2)
+        self.T1X = self.get_info(self._Flag.T1X)
+        self.T1S = self.get_info(self._Flag.T1S)
+        self.T2X = self.get_info(self._Flag.T2X)
+        self.T2S = self.get_info(self._Flag.T2S)
+        self.T3X = self.get_info(self._Flag.T3X)
+        self.T3S = self.get_info(self._Flag.T3S)
+        self.HasVelo = self.get_info(self._Flag.HasVelo)
+        self.HasTT = self.get_info(self._Flag.HasTT)
+        self.HasT1 = self.get_info(self._Flag.HasT1)
+        self.HasT2 = self.get_info(self._Flag.HasT2)
+        self.HasT3 = self.get_info(self._Flag.HasT3)
+        self.HasT = self.get_info(self._Flag.HasT)
+        self.HasVeloAndT = self.get_info(self._Flag.HasVeloAndT)
+        self.AccVeloR = self.get_info(self._Flag.AccVeloR)
+        self.AccVeloPhi = self.get_info(self._Flag.AccVeloPhi)
+        self.AccTT1 = self.get_info(self._Flag.AccTT1)
+        self.AccTT2 = self.get_info(self._Flag.AccTT2)
+        self.AccT1X = self.get_info(self._Flag.AccT1X)
+        self.AccT1S = self.get_info(self._Flag.AccT1S)
+        self.AccT2X = self.get_info(self._Flag.AccT2X)
+        self.AccT2S = self.get_info(self._Flag.AccT2S)
+        self.AccT3X = self.get_info(self._Flag.AccT3X)
+        self.AccT3S = self.get_info(self._Flag.AccT3S)
+        self.AccVelo = self.get_info(self._Flag.AccVelo)
+        self.AccTT = self.get_info(self._Flag.AccTT)
+        self.AccT1 = self.get_info(self._Flag.AccT1)
+        self.AccT2 = self.get_info(self._Flag.AccT2)
+        self.AccT3 = self.get_info(self._Flag.AccT3)
+        self.AccT = self.get_info(self._Flag.AccT)
+        self.AccVeloAndT = self.get_info(self._Flag.AccVeloAndT)
+        self.multVeloR = self.get_info(self._Flag.multVeloR)
+        self.multVeloPhi = self.get_info(self._Flag.multVeloPhi)
+        self.MultVeloR = self.get_info(self._Flag.MultVeloR)
+        self.MultVeloPhi = self.get_info(self._Flag.MultVeloPhi)
+
+        # UT alias
+        self.UT1 = self.TT1
+        self.UT2 = self.TT2
+        self.HasUT = self.HasTT
+        self.AccUT1 = self.AccTT1
+        self.AccUT2 = self.AccTT2
+        self.AccUT = self.AccTT
+
+        # Create functors for MC_PROPERTY
+        self.Property = F.MAP_INPUT(
+            Functor=F.MC_PROPERTY, Relations=self.MC2MCTrackInfo)
+
+        # Reconstructible
+        self.Reconstructible = F.MAP_INPUT(
+            Functor=F.MC_RECONSTRUCTIBLE, Relations=self.MC2Reconstructible)
+
+
+class MCP2Track:
+    """
+    MCP2Track helper class, will create all possible Reconstructed Track related functors and can be
+    accessed as the standard python class member, the member function Get(F) is a flexible
+    interface for future update.
+
+    Args:
+        mcparticles_input : MCParticle data handle (TES)
+        relations_locs: TES locations to the pre-existing relations for charged and neutral particles.
+            default = ["Relations/ChargedPP2MCP", "Relations/NeutralPP2MCP"]
+        process     (optional) : Process type (Hlt2 or Spruce), default = 'Hlt2'
+        root_in_tes (optional) : Standard Gaudi Algorithm RootInTES location
+        output_level (optional) : Standard Gaudi Algorithm OutputLevel
+
+    Example:
+        # Create the helper
+        MC_TR = MC_Track(MCParticles)
+
+        # Use predefined functor
+        allvariables['HAST'] = MC_TR.HasT 
+
+        # or use general interface, equivalent as above
+        allvariables['HAST'] = MC_TR.Get(F.TRACKHAST)
+
+    """
+
+    # Flexible interface for possible update
+    def get_info(self, Functor: BoundFunctor):
+        return F.MAP_INPUT(Functor=Functor, Relations=self.MC2Track)
+
+    def __init__(self,
+                 mcparticles_input,
+                 relations_locs=[
+                     "Relations/ChargedPP2MCP", "Relations/NeutralPP2MCP"
+                 ],
+                 process='Spruce',
+                 root_in_tes=None,
+                 output_level=INFO):
+
+        if not root_in_tes:
+            if process == 'Spruce':
+                root_in_tes = '/Event/Spruce/HLT2'
+            elif process == 'Hlt2' or process == 'Turbo':
+                root_in_tes = '/Event/HLT2'
+            else:
+                raise ValueError(
+                    f"The specified 'process' {process} not recognised. Can only be 'Hlt2' or 'Spruce' or 'Turbo'. Please check!"
+                )
+
+        if root_in_tes:
+            relations_locs = [
+                f'{root_in_tes}/{table_loc}' for table_loc in relations_locs
+            ]
+        ChargedPP2MCP = force_location(relations_locs[0])
+        NeutralPP2MCP = force_location(relations_locs[1])
+
+        # Load Algorithm
+        self.Algorithm = MC2TrackAlg(
+            Input=mcparticles_input,
+            ChargedPP2MCP=ChargedPP2MCP,
+            NeutralPP2MCP=NeutralPP2MCP,
+            OutputLevel=output_level)
+
+        # Alias the outputs
+        self.MC2Reconstructed = self.Algorithm.MC2Reconstructed
+        self.MC2Track = self.Algorithm.MC2Track
+
+        # Reconstructed
+        self.Reconstructed = F.MAP_INPUT(
+            Functor=F.MC_RECONSTRUCTED, Relations=self.MC2Reconstructed)
+
+        # Predefined track related functor
+        self.ReferencePoint = self.get_info(F.REFERENCEPOINT)
+        self.ReferencePoint_X = self.get_info(F.REFERENCEPOINT_X)
+        self.ReferencePoint_Y = self.get_info(F.REFERENCEPOINT_Y)
+        self.ReferencePoint_Z = self.get_info(F.REFERENCEPOINT_Z)
+        self.nDoF = self.get_info(F.NDOF)
+        self.GhostProbability = self.get_info(F.GHOSTPROB)
+        self.Flag = self.get_info(F.TRACKFLAG)
+        self.Type = self.get_info(F.TRACKTYPE)
+        self.HasT = self.get_info(F.TRACKHAST)
+        self.HasUT = self.get_info(F.TRACKHASUT)
+        self.HasVelo = self.get_info(F.TRACKHASVELO)
+        self.nHits = self.get_info(F.NHITS)
+        self.nVPHits = self.get_info(F.NVPHITS)
+        self.nUTHits = self.get_info(F.NUTHITS)
+        self.nFTHits = self.get_info(F.NFTHITS)
+        self.History = self.get_info(F.TRACKHISTORY)
diff --git a/Phys/DaVinci/python/DaVinci/truth_matching.py b/Phys/DaVinci/python/DaVinci/truth_matching.py
index 089ae7e6f..905688f04 100644
--- a/Phys/DaVinci/python/DaVinci/truth_matching.py
+++ b/Phys/DaVinci/python/DaVinci/truth_matching.py
@@ -23,6 +23,7 @@ def configured_MCTruthAndBkgCatAlg(
         process='Spruce',
         root_in_tes=None,
         redo_neutral_assoc=False,
+        filter_MCP=True,
         output_level=INFO):
     """
     Function to help configure the tools instantiated by the `MCTruthAndBkgCatAlg` algorithm.
@@ -58,11 +59,11 @@ def configured_MCTruthAndBkgCatAlg(
     if not root_in_tes:
         if process == 'Spruce':
             root_in_tes = '/Event/Spruce/HLT2'
-        elif process == 'Hlt2':
+        elif process == 'Hlt2' or process == 'Turbo':
             root_in_tes = '/Event/HLT2'
         else:
             raise ValueError(
-                f"The specified 'process' {process} not recognised. Can only be 'Hlt2' or 'Spruce'. Please check!"
+                f"The specified 'process' {process} not recognised. Can only be 'Hlt2' or 'Spruce' or 'Turbo'. Please check!"
             )
 
     # Tool used by DaVinciSmartAssociator
@@ -95,6 +96,7 @@ def configured_MCTruthAndBkgCatAlg(
 
     mctruth = MCTruthAndBkgCatAlg(
         Input=inputs,
+        filter_MCP=filter_MCP,
         DaVinciSmartAssociator=dv_assc,
         MCMatchObjP2MCRelator=mcrel_assc,
         BackgroundCategory=bkg_cat,
-- 
GitLab