From 191e8f566d75ec3ad55234e372234dd1a59a010c Mon Sep 17 00:00:00 2001
From: amathad <Abhijit Mathad amathad@cern.ch>
Date: Tue, 2 Mar 2021 10:22:07 +0100
Subject: [PATCH 1/6] Tuple algorithm, moving from Phys to Analysis, removing
 LoKiGenMC mentions

---
 Phys/FunTuple/CMakeLists.txt                  |  36 ++
 Phys/FunTuple/README.md                       |   1 +
 Phys/FunTuple/options/FunTuple_opts.py        | 125 +++++
 Phys/FunTuple/src/FunTuple.cpp                | 465 ++++++++++++++++++
 Phys/FunTuple/src/MakeData.cpp                | 233 +++++++++
 Phys/FunTuple/src/ParticleTupleProp.cpp       |  47 ++
 Phys/FunTuple/src/ParticleTupleProp.h         |  57 +++
 .../qmtest/algorithms.qms/test-funtuple.qmt   |  35 ++
 .../tests/qmtest/options/FunTuple_opts.py     | 125 +++++
 9 files changed, 1124 insertions(+)
 create mode 100644 Phys/FunTuple/CMakeLists.txt
 create mode 100644 Phys/FunTuple/README.md
 create mode 100755 Phys/FunTuple/options/FunTuple_opts.py
 create mode 100755 Phys/FunTuple/src/FunTuple.cpp
 create mode 100755 Phys/FunTuple/src/MakeData.cpp
 create mode 100755 Phys/FunTuple/src/ParticleTupleProp.cpp
 create mode 100755 Phys/FunTuple/src/ParticleTupleProp.h
 create mode 100644 Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt
 create mode 100755 Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py

diff --git a/Phys/FunTuple/CMakeLists.txt b/Phys/FunTuple/CMakeLists.txt
new file mode 100644
index 000000000..c87a27354
--- /dev/null
+++ b/Phys/FunTuple/CMakeLists.txt
@@ -0,0 +1,36 @@
+###############################################################################
+# (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.                                       #
+###############################################################################
+
+gaudi_subdir(FunTuple)
+
+gaudi_depends_on_subdirs(Phys/DaVinciKernel
+                         Phys/DaVinciMCKernel
+			 			 Kernel/LHCbMath
+                         Phys/LoKi
+                         Phys/LoKiMC
+                         Phys/LoKiAlgoMC
+                         Phys/LoKiArrayFunctors
+                         Phys/LoKiPhys
+                         )
+
+find_package(Boost)
+find_package(ROOT COMPONENTS RIO Tree)
+include_directories(SYSTEM ${ROOT_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
+
+gaudi_install_headers(CombKernel)
+
+gaudi_add_module(FunTuple
+                 src/*.cpp
+                 LINK_LIBRARIES DaVinciKernelLib DaVinciMCKernelLib LHCbMathLib LoKiAlgoMCLib LoKiArrayFunctorsLib Boost LoKiPhysLib LoKiPhysMCLib ROOT)
+
+gaudi_add_test(QMTest QMTEST)
+
+gaudi_install_python_modules()
diff --git a/Phys/FunTuple/README.md b/Phys/FunTuple/README.md
new file mode 100644
index 000000000..01f674cc6
--- /dev/null
+++ b/Phys/FunTuple/README.md
@@ -0,0 +1 @@
+New nTupling algorithm 
diff --git a/Phys/FunTuple/options/FunTuple_opts.py b/Phys/FunTuple/options/FunTuple_opts.py
new file mode 100755
index 000000000..d7e049153
--- /dev/null
+++ b/Phys/FunTuple/options/FunTuple_opts.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env gaudirun.py
+###############################################################################
+# (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.                                       #
+###############################################################################
+
+
+######### Define helpful classes and functions
+class ParticleTupleProp:
+    def __init__(self, branch_name, decay_descriptor, particle_code):
+        """
+        branch_name     : Branch name of particle
+        decay_descriptor: Decay descriptor
+        particle_code   : List of tuples. Each tuple contains strings defining
+                         (Functorcode, corresponding branch name, C++ return type that could handle precision, description).
+        """
+        self.branch_name = branch_name
+        self.decay_descriptor = decay_descriptor
+        self.particle_code = particle_code
+
+    def to_dict(self):
+        return {self.branch_name: {self.decay_descriptor: self.particle_code}}
+
+
+def convert_to_parsable_objs(particle_tuple_props):
+    """
+    particle_tuple_props: List of ParticleTupleProp
+    """
+
+    def get_lists(ptps, i):
+        f_list = []
+        for ptp in ptps:
+            f_list += [[pc[i] for pc in ptp.particle_code]]
+
+        return f_list
+
+    branch_list = [ptp.branch_name for ptp in particle_tuple_props]
+    discrp_list = [ptp.decay_descriptor for ptp in particle_tuple_props]
+    func_list = get_lists(particle_tuple_props, 0)
+    func_bnamelist = get_lists(particle_tuple_props, 1)
+    func_rtypelist = get_lists(particle_tuple_props, 2)
+
+    return branch_list, discrp_list, func_list, func_bnamelist, func_rtypelist
+
+
+#########
+
+from Gaudi.Configuration import *
+from Configurables import LHCbApp
+from Configurables import LHCb__ParticlePropertySvc
+LHCb__ParticlePropertySvc(
+)  #To turn on verbose set LHCb__ParticlePropertySvc(OutputLevel=1)
+from Configurables import CondDB
+CondDB(Upgrade=True)
+
+app = LHCbApp()
+app.DataType = "Upgrade"
+app.DDDBtag = "dddb-20171126"
+app.CondDBtag = "sim-20171127-vc-md100"
+app.Simulation = True
+ApplicationMgr().EvtMax = 5
+ApplicationMgr().EvtSel = "NONE"
+
+#Make MCParticles (just one Bs): Turns out TES is not clever enough to take ownership 'recursively' (set makeCustomData = True if running this algorithm)
+from Configurables import MakeData
+algMakeData = MakeData(
+    name="Alg_MakeData",
+    decay_descriptor=
+    "[B_s0 => (J/psi(1S) => mu+ mu- ) ( phi(1020) => K+ K-)]CC",
+    output_location="MCParticle")
+
+#Do we need a custom gaudi property like ParticleTupleProp?
+#Configure Particles Bs
+ParticleBs = ParticleTupleProp(
+    branch_name="Bs",
+    decay_descriptor=
+    "[B_s0 => (J/psi(1S) => mu+ mu- ) ( phi(1020) => K+ K-)]CC",
+    #List of tuple, each tuple (functor, branch name, precision (TODO), description).
+    particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
+                   ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
+#Configure Particles Phi
+ParticlePhi = ParticleTupleProp(
+    branch_name="Phi",
+    decay_descriptor=
+    "[B_s0 => (J/psi(1S) => mu+ mu- ) ^( phi(1020) => K+ K-)]CC",
+    particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
+                   ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
+#Do not need to do this if a custom gaudi property is implemented
+branchlist, decaydiscrpList, funcList, funcBranchNameList, funcRetunTypeList = convert_to_parsable_objs(
+    [ParticleBs, ParticlePhi])
+
+from Configurables import FunTuple
+algtupledata = FunTuple(
+    name="FunTuple",
+    tree_name="DecayTree",
+    branch_names=branchlist,
+    is_MC=True,  #Since passing MCParticles
+    decay_descriptors=decaydiscrpList,
+    functors=funcList,
+    functor_branch_names=funcBranchNameList,
+    functor_return_types=funcRetunTypeList,
+    make_custom_data=
+    True,  #Only set to True since running over MakeData algo (produces its own Bs->Jpsiphi MC decays).
+    input_location="MCParticle",
+    NTupleLUN='tuple')
+
+ApplicationMgr().TopAlg = [algMakeData, algtupledata]
+
+#Tuple props
+outputname = "FunTuple.root"
+NTupleSvc().Output = [
+    "{} DATAFILE='{}' TYP='ROOT' OPT='NEW'".format(algtupledata.NTupleLUN,
+                                                   outputname)
+]
+ApplicationMgr().ExtSvc.append(NTupleSvc())
+ApplicationMgr().HistogramPersistency = "ROOT"
+# Set the compression level for the ROOT tuple file
+from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
+RFileCnv('RFileCnv').GlobalCompression = "LZMA:6"
diff --git a/Phys/FunTuple/src/FunTuple.cpp b/Phys/FunTuple/src/FunTuple.cpp
new file mode 100755
index 000000000..c2de2c88c
--- /dev/null
+++ b/Phys/FunTuple/src/FunTuple.cpp
@@ -0,0 +1,465 @@
+/*****************************************************************************\
+* (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.                                       *
+\*****************************************************************************/
+
+// Gaudi
+#include "GaudiAlg/Consumer.h"
+#include "GaudiAlg/GaudiTupleAlg.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/Point3DTypes.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/Vector3DTypes.h"
+#include "GaudiKernel/Vector4DTypes.h"
+
+// Event
+#include "Event/MCParticle.h"
+#include "Event/MCVertex.h"
+#include "Event/Particle.h"
+
+// LoKi
+#include "LoKi/IDecay.h"
+#include "LoKi/IHybridFactory.h"
+#include "LoKi/IMCDecay.h"
+#include "LoKi/IMCHybridFactory.h"
+#include "LoKi/Trees.h"
+
+// Kernel
+#include "Kernel/IDecayFinder.h"
+#include "Kernel/IParticlePropertySvc.h"
+#include "Kernel/ParticleID.h"
+#include "Kernel/ParticleProperty.h"
+
+// boost
+#include <boost/algorithm/string/join.hpp>
+
+// custom class
+#include "ParticleTupleProp.h"
+
+namespace LHCb {
+  namespace FTuple {
+    template <class T>
+    using Consumer =
+        Gaudi::Functional::Consumer<void( const T& ), Gaudi::Functional::Traits::BaseClass_t<GaudiTupleAlg>>;
+    typedef std::vector<std::string> TUPLE;
+    typedef std::vector<TUPLE>       TUPLELIST;
+    typedef std::vector<MCParticle>  MCPARTICLES;
+    typedef std::vector<Particle>    PARTICLES;
+  } // namespace FTuple
+} // namespace LHCb
+
+template <class T>
+class FunTuple : public LHCb::FTuple::Consumer<T> {
+public:
+  FunTuple( const std::string& name, ISvcLocator* pSvc );
+  virtual StatusCode initialize() override;
+  virtual void       operator()( const T& particles ) const override;
+
+protected:
+  template <typename T2>
+  StatusCode booktuple( const Tuples::Tuple& m_ntuple, const std::string& colname, const T2& val ) const;
+  StatusCode checks();
+  StatusCode setParticleTupleProps();
+  void       FindAndBookTuple( const unsigned int& i, const T& particles, const Tuples::Tuple& ntuple ) const {}
+  //(Will be removed): The following overloaded function (makeMCTwoBodyDecay) are only used when m_makeCustomData is set
+  // to true (see m_makeCustomData property description for more info)
+  void makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2, const int& parentid ) const;
+  void makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2, const int& parentid,
+                           const int& prod1id, const int& prod2id, MsgStream& log ) const;
+
+private:
+  Gaudi::Property<std::string> m_ntupleTname{this, "tree_name", "DecayTree", "Set TTree name"};
+  Gaudi::Property<bool>        m_isMC{this, "is_MC", true, "Set is MC TRUTH or not"};
+  // After defining custom Gaudi property remove m_branchnames, m_decaydescriptors, m_functors, m_funcbranchnames,
+  // m_funcreturntypes  (Do we need the custom Gaudi::Property like  ParticleTupleProp?)
+  Gaudi::Property<LHCb::FTuple::TUPLE>     m_branchnames{this, "branch_names", LHCb::FTuple::TUPLE(),
+                                                     "List of TBranch names"};
+  Gaudi::Property<LHCb::FTuple::TUPLE>     m_decaydescriptors{this, "decay_descriptors", LHCb::FTuple::TUPLE(),
+                                                          "List of DecayDescriptors"};
+  Gaudi::Property<LHCb::FTuple::TUPLELIST> m_functors{this, "functors", LHCb::FTuple::TUPLELIST(),
+                                                      "List of functor names"};
+  Gaudi::Property<LHCb::FTuple::TUPLELIST> m_funcbranchnames{this, "functor_branch_names", LHCb::FTuple::TUPLELIST(),
+                                                             "List of functor TBranch names"};
+  // TODO: Could m_funcreturntypes propery handle the user requested precision on the variables being tupled?
+  Gaudi::Property<LHCb::FTuple::TUPLELIST>  m_funcreturntypes{this, "functor_return_types", LHCb::FTuple::TUPLELIST(),
+                                                             "List of functor return types"};
+  Gaudi::Property<std::vector<std::string>> m_preamble_def{this, "Preamble", {}, "List of preamble strings"};
+  Gaudi::Property<bool>                     m_makeCustomData{
+      this, "make_custom_data", true,
+      "(Property for testing only, will be removed) Should only be set to true if you ran over MakeData algorithm before hand. \
+												 If set to true, makes Bs->Jpsi(mumu)phi(KK)\
+												 Feature introduced because when Bs head particle is built in MakeData algorithm and put into TES, and then is passed into this algorithm \
+												 the products of Bs are garbage. Turns out TES is not clever enough to take ownership 'recursively'"};
+  // vector of ParticleTupleProp custom objects. These are built out of above member variables (this is the object I
+  // want the Gaudi::Property to be?)
+  std::vector<ParticleTupleProp> m_particletupleprops;
+  // for Particle Loki functors
+  std::string                                m_preamble;
+  ToolHandle<LoKi::IHybridFactory>           m_factory = {"LoKi::Hybrid::Tool/HybridFactory:PUBLIC", this};
+  LoKi::Types::Fun                           m_fun{LoKi::Constant<const LHCb::Particle*, double>( -1.0e+10 )};
+  std::vector<std::vector<LoKi::Types::Fun>> m_vfun;
+  // for Particle decay descriptor matching
+  ToolHandle<Decays::IDecay> m_decaytool = {this, "Decay", "LoKi::Decay"};
+  Decays::IDecay::Tree       m_decayTree = Decays::Trees::Invalid_<const LHCb::Particle*>();
+  // for MCParticle Loki functors
+  ToolHandle<LoKi::IMCHybridFactory>           m_mcfactory = {"LoKi::Hybrid::MCTool/MCHybridFactory:PUBLIC", this};
+  LoKi::Types::MCFun                           m_mcfun{LoKi::Constant<const LHCb::MCParticle*, double>( -1.0e+10 )};
+  std::vector<std::vector<LoKi::Types::MCFun>> m_vmcfun;
+  // for MCParticle decay descriptor matching
+  ToolHandle<Decays::IMCDecay> m_mcdecaytool = {this, "MCDecay", "LoKi::MCDecay"};
+  Decays::IMCDecay::Tree       m_mcdecayTree = Decays::Trees::Invalid_<const LHCb::MCParticle*>();
+  // property service
+  ServiceHandle<LHCb::IParticlePropertySvc> m_particlePropertySvc{this, "ParticleProperty",
+                                                                  "LHCb::ParticlePropertySvc"};
+};
+
+template <class T>
+FunTuple<T>::FunTuple( const std::string& name, ISvcLocator* pSvc )
+    : LHCb::FTuple::Consumer<T>( name, pSvc, {"input_location", {"MyParticle"}} ) {}
+
+template <class T>
+StatusCode FunTuple<T>::initialize() {
+
+  // initialise consumer
+  StatusCode sc_init = LHCb::FTuple::Consumer<T>::initialize();
+  if ( sc_init.isFailure() ) { return this->Error( "Error in initialisation of consumer" ); }
+
+  // conduct checks to see if the parsed arguments are ok
+  StatusCode sc_check = checks();
+  if ( sc_check.isFailure() ) { return this->Error( "Error in checks" ); }
+
+  // Using the arguments make a list of ParticleTupleProp objects that hold info on (TBranch name, Decay descriptor,
+  // List of FunctorProp objects). The FunctorProp objects hold info on (functor name, functor TBranch name, functor
+  // return type (can handle precision?))
+  StatusCode sc_ptp = setParticleTupleProps();
+  if ( sc_ptp.isFailure() ) { return this->Error( "Error in setting ParticleTupleProps" ); }
+
+  // Instantiate all the functors (unfortunately the FunctorProp objects created in the previous step could not hold
+  // this information).
+  m_preamble = boost::algorithm::join( m_preamble_def.value(), "\n" );
+  for ( const auto& ptp : m_particletupleprops ) {
+    std::vector<LoKi::Types::MCFun> imfunc_mc;
+    std::vector<LoKi::Types::Fun>   imfunc;
+    for ( const auto& tup : ptp.FunctorProps() ) {
+      if ( m_isMC ) { // MC Particles
+        StatusCode sc_func_mc = m_mcfactory->get( tup.Func, m_mcfun, m_preamble );
+        if ( sc_func_mc.isFailure() ) {
+          return this->Error( "Error in MC instantiating functor in particle branch " + ptp.BranchName() +
+                              " with functor branch name " + tup.Func_branchname );
+        }
+        imfunc_mc.push_back( m_mcfun );
+        // this->info() << "Created a function in " + ptp.BranchName() + " with " + tup.Func_branchname + " : The
+        // function is '" << imfunc_mc.back() << "'" << endmsg;
+      } else { // Particles
+        StatusCode sc_func = m_factory->get( tup.Func, m_fun, m_preamble );
+        if ( sc_func.isFailure() ) {
+          return this->Error( "Error in instantiating functor in particle branch " + ptp.BranchName() +
+                              " with functor branch name " + tup.Func_branchname );
+        }
+        imfunc.push_back( m_fun );
+        // this->info() << "Created a function in " + ptp.BranchName() + " with " + tup.Func_branchname + " : The
+        // function is '" << imfunc.back() << "'" << endmsg;
+      }
+    }
+    if ( m_isMC ) { // MC Particles related
+      m_vmcfun.push_back( imfunc_mc );
+    } else { // Particles related
+      m_vfun.push_back( imfunc );
+    }
+  }
+  return StatusCode::SUCCESS;
+}
+
+template <class T>
+StatusCode FunTuple<T>::checks() {
+  // check tuple name
+  if ( m_ntupleTname.empty() ) { return this->Error( "Error tree name is empty" ); }
+
+  // check related to containers
+  if ( m_branchnames.empty() ) { return this->Error( "Error branch names container is empty" ); }
+  if ( m_decaydescriptors.empty() ) { return this->Error( "Error decay descriptor container is empty" ); }
+  if ( m_functors.empty() ) { return this->Error( "Error functor container is empty" ); }
+  if ( m_funcbranchnames.empty() ) { return this->Error( "Error functor branch names container is empty" ); }
+  if ( m_funcreturntypes.empty() ) { return this->Error( "Error functor return types container is empty" ); }
+  if ( m_branchnames.size() != m_decaydescriptors.size() ) {
+    return this->Error( "Error size of branch names and decay descriptors containers do not match" );
+  }
+  if ( m_branchnames.size() != m_functors.size() ) {
+    return this->Error( "Error size of branch names and functor lists containers do not match" );
+  }
+  if ( m_branchnames.size() != m_funcbranchnames.size() ) {
+    return this->Error( "Error size of branch names and functor branch names containers do not match" );
+  }
+  if ( m_branchnames.size() != m_funcreturntypes.size() ) {
+    return this->Error( "Error size of branch names and functor return types containers do not match" );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+template <class T>
+StatusCode FunTuple<T>::setParticleTupleProps() {
+  // make a list of ParticleTupleProp objects to hold info related to particle being tupled
+  for ( unsigned int i = 0; i < m_branchnames.size(); i++ ) {
+    ParticleTupleProp ptp = ParticleTupleProp();
+    ptp.setBranchName( m_branchnames[i] );
+    ptp.setDecayDescriptor( m_decaydescriptors[i] );
+    // make a list of FunctorProp objects to hold functor properties
+    ptp.setFunctorProps( m_functors[i], m_funcbranchnames[i], m_funcreturntypes[i] );
+    // conduct checks on whether input are empty or not
+    if ( !ptp.checks() ) { return this->Error( "Error conducting checks with ParticleTupleProp" ); }
+    m_particletupleprops.push_back( ptp );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+template <class T>
+template <typename T2>
+StatusCode FunTuple<T>::booktuple( const Tuples::Tuple& ntuple, const std::string& colname, const T2& val ) const {
+  return ntuple->column( colname, val );
+}
+
+template <class T>
+void FunTuple<T>::operator()( const T& particles ) const {
+  // make ntuple
+  Tuples::Tuple ntuple = this->nTuple( m_ntupleTname );
+
+  // loop over ParticleTupleProp (i.e. individual decay descriptors to get the particles)
+  for ( unsigned int i = 0; i < m_particletupleprops.size(); i++ ) { FindAndBookTuple( i, particles, ntuple ); }
+
+  // write the tuple
+  StatusCode sc_write = ntuple->write();
+  if ( sc_write.isFailure() ) { this->err() << "Unable to write the tuple " << endmsg; }
+}
+
+template <>
+void FunTuple<LHCb::FTuple::MCPARTICLES>::FindAndBookTuple( const unsigned int&              i,
+                                                            const LHCb::FTuple::MCPARTICLES& particles,
+                                                            const Tuples::Tuple&             ntuple ) const {
+  // Messaging service
+  MsgStream log = this->info();
+  MsgStream err = this->err();
+
+  // check that correct functors related to MCParticles are loaded
+  // TODO: MC Truth association with the reco particles needs to be handled
+  if ( !m_isMC ) { err << "Have not asked for MC but passed LHCb::MCParticles" << endmsg; }
+
+  // Get the tuple properties
+  ParticleTupleProp ptp = m_particletupleprops[i];
+
+  // parse the decay descriptor for MC Truth
+  const Decays::IMCDecay::Tree decayTree = m_mcdecaytool->tree( ptp.DecayDescriptor() );
+  if ( !decayTree ) { err << "Could not find MC decayTree for " + ptp.DecayDescriptor() << endmsg; }
+  log << "MC decay descriptor parsed is " << decayTree << endmsg;
+
+  // create the decay finder  for MC Truth
+  Decays::IMCDecay::Finder finder( decayTree );
+  if ( !finder ) { err << "Unable to create MC decay finder'" << endmsg; }
+
+  // make input and output constvector
+  LHCb::MCParticle::ConstVector output;
+  LHCb::MCParticle::ConstVector input;
+
+  // Make new particles (will be removed) only used when m_makeCustomData is True (see m_makeCustomData property
+  // description for more info)
+  LHCb::MCParticle p1   = LHCb::MCParticle();
+  LHCb::MCParticle p1_a = LHCb::MCParticle();
+  LHCb::MCParticle p1_b = LHCb::MCParticle();
+  LHCb::MCParticle p2   = LHCb::MCParticle();
+  LHCb::MCParticle p2_a = LHCb::MCParticle();
+  LHCb::MCParticle p2_b = LHCb::MCParticle();
+  LHCb::MCParticle p    = LHCb::MCParticle();
+  if ( m_makeCustomData ) {
+    // set their properties of new particles
+    makeMCTwoBodyDecay( p1, p1_a, p1_b, 443, 13, -13, err );   // Jpis -> mu- mu+
+    makeMCTwoBodyDecay( p2, p2_a, p2_b, 333, 321, -321, err ); // phi -> K+ K-
+    makeMCTwoBodyDecay( p, p1, p2, 531 );                      // Bs -> Jpsi phi
+    input.push_back( &p );
+  } else {
+    // fill the input vector from the input directly
+    for ( const auto& p : particles ) { input.push_back( &p ); }
+  }
+
+  // find the MC decay
+  finder.findDecay( input.begin(), input.end(), output );
+
+  // TODO: Need to handle multiple cands. For now just throw an error
+  if ( output.size() > 1 ) { err << "Multiple particles match the decay descriptor " << endmsg; }
+  log << "Found #" << output.size() << " decays" << endmsg;
+  // for (const auto& p: output){log << *p << endmsg;}
+
+  // loop over FunctorProp to get individual functor outputs
+  for ( unsigned int j = 0; j < ( ptp.FunctorProps() ).size(); j++ ) {
+    ParticleProp::FunctorProp functup    = ( ptp.FunctorProps() )[j];
+    std::string               branchname = ptp.BranchName() + "_" + functup.Func_branchname;
+    double                    val        = m_vmcfun[i][j]( output[0] );
+    // log << "Had created a function '" << mcfunct << "'" << endmsg;
+    // log << "value function '" << val << "'" << endmsg;
+
+    // book tuple
+    StatusCode sc_book = booktuple( ntuple, branchname, val );
+    if ( sc_book.isFailure() ) { err << "Unable to book the columns " << endmsg; }
+  }
+}
+
+template <>
+void FunTuple<LHCb::FTuple::PARTICLES>::FindAndBookTuple( const unsigned int&            i,
+                                                          const LHCb::FTuple::PARTICLES& particles,
+                                                          const Tuples::Tuple&           ntuple ) const {
+  // Messaging service
+  MsgStream log = this->info();
+  MsgStream err = this->err();
+
+  // check that correct functors related to Particles are loaded
+  // TODO: MC Truth association with the reco particles needs to be handled
+  if ( m_isMC ) { err << "Asked for MC but passed LHCb::Particles" << endmsg; }
+
+  // Get the tuple properties
+  ParticleTupleProp ptp = m_particletupleprops[i];
+
+  // parse the decay descriptor for reco
+  const Decays::IDecay::Tree decayTree = m_decaytool->tree( ptp.DecayDescriptor() );
+  if ( !decayTree ) { err << "Could not find decayTree for " + ptp.DecayDescriptor() << endmsg; }
+  log << "Decay descriptor parsed is " << decayTree << endmsg;
+
+  // create the decay finder for reco
+  Decays::IDecay::Finder finder( decayTree );
+  if ( !finder ) { err << "Unable to create decay finder'" << endmsg; }
+
+  // make input and output constvector
+  LHCb::Particle::ConstVector output;
+  LHCb::Particle::ConstVector input;
+  for ( const auto& p : particles ) { input.push_back( &p ); }
+  finder.findDecay( input.begin(), input.end(), output );
+
+  // TODO: Need to handle multiple cands. For now just throw an error
+  if ( output.size() > 1 ) { this->err() << "Multiple particles match the decay descriptor " << endmsg; }
+  this->info() << "Found #" << output.size() << " decays" << endmsg;
+  // for (const auto& p: output){log << *p << endmsg;}
+
+  for ( unsigned int j = 0; j < ( ptp.FunctorProps() ).size(); j++ ) {
+    ParticleProp::FunctorProp functup    = ( ptp.FunctorProps() )[j];
+    std::string               branchname = ptp.BranchName() + "_" + functup.Func_branchname;
+    double                    val        = m_vfun[i][j]( output[0] );
+    // log << "value function '" << val << "'" << endmsg;
+
+    // book tuple
+    StatusCode sc_book = booktuple( ntuple, branchname, val );
+    if ( sc_book.isFailure() ) { err << "Unable to book the columns " << endmsg; }
+  }
+}
+
+// This function is for testing purpose only and will be used only when m_makeCustomData is True, otherwise useless (see
+// makeMCTwoBodyDecay property description for more info)
+template <class T>
+void FunTuple<T>::makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2,
+                                      const int& parentid, const int& prod1id, const int& prod2id,
+                                      MsgStream& log ) const {
+  using std::sqrt;
+
+  // particle id of products
+  const LHCb::ParticleID p1_id = LHCb::ParticleID( prod1id ); // mu+
+  const LHCb::ParticleID p2_id = LHCb::ParticleID( prod2id ); // mu-
+  p1.setParticleID( p1_id );
+  p2.setParticleID( p2_id );
+  // four momenta of products
+  const LHCb::ParticleProperty* p1_prop = m_particlePropertySvc->find( p1_id );
+  const LHCb::ParticleProperty* p2_prop = m_particlePropertySvc->find( p2_id );
+  if ( !p1_prop || !p2_prop ) { log << "Could not find either p1_prop or p2_prop" << endmsg; }
+  const double               p1_mass = p1_prop->mass();
+  const double               p2_mass = p2_prop->mass();
+  const Gaudi::XYZVector     p1_3vec{10.2, 10.9, 30.2};
+  const Gaudi::XYZVector     p2_3vec{20.5, 20.5, 60.6};
+  double                     p1_E    = sqrt( p1_3vec.Mag2() + p1_mass * p1_mass );
+  double                     p2_E    = sqrt( p2_3vec.Mag2() + p2_mass * p2_mass );
+  const Gaudi::LorentzVector p1_4vec = Gaudi::LorentzVector( p1_3vec.X(), p1_3vec.Y(), p1_3vec.Z(), p1_E );
+  const Gaudi::LorentzVector p2_4vec = Gaudi::LorentzVector( p2_3vec.X(), p2_3vec.Y(), p2_3vec.Z(), p2_E );
+  p1.setMomentum( p1_4vec );
+  p2.setMomentum( p2_4vec );
+
+  // Define origin vertex of parent (DecayVertex)
+  SmartRef<LHCb::MCVertex> p_orgvertx = new LHCb::MCVertex();
+  const Gaudi::XYZPoint    p_orgvrtx{2.8, -1.5, 1.3};
+  p_orgvertx->setPosition( p_orgvrtx );
+  p_orgvertx->setTime( 0.05 );
+  p_orgvertx->setType( LHCb::MCVertex::DecayVertex );
+
+  // Define end vertices of parent
+  const Gaudi::XYZPoint            p_endvrtx{6.8, -4.5, 3.3};
+  SmartRefVector<LHCb::MCParticle> prods_endvertx;
+  prods_endvertx.push_back( &p1 );
+  prods_endvertx.push_back( &p2 );
+  SmartRef<LHCb::MCVertex> p_endvertx = new LHCb::MCVertex();
+  p_endvertx->setPosition( p_endvrtx );
+  p_endvertx->setTime( 0.13 );
+  p_endvertx->setType( LHCb::MCVertex::DecayVertex );
+  p_endvertx->setProducts( prods_endvertx ); // products in this vertex (particle 1 and 2)
+  SmartRefVector<LHCb::MCVertex> p_endvertxs;
+  p_endvertxs.push_back( p_endvertx );
+
+  // set properties of parent
+  const LHCb::ParticleID p_id = LHCb::ParticleID( parentid ); // Jpsi
+  p.setParticleID( p_id );
+  p.setMomentum( p1_4vec + p2_4vec );
+  p.setOriginVertex( p_orgvertx );
+  p.setEndVertices( p_endvertxs );
+
+  /*
+  this->info() << p << endmsg;
+  for (const auto& mcv: p.endVertices()){
+          this->info() << *mcv << endmsg;
+          for (const auto& mcd: (*mcv).products()){this->info() << *mcd << endmsg;}
+  }
+  */
+}
+
+// This function is for testing purpose only and will be used only when m_makeCustomData is True, otherwise useless (see
+// makeMCTwoBodyDecay property description for more info)
+template <class T>
+void FunTuple<T>::makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2,
+                                      const int& parentid ) const {
+  // get 4-momenta
+  const Gaudi::LorentzVector p1_4vec = p1.momentum();
+  const Gaudi::LorentzVector p2_4vec = p2.momentum();
+
+  // Define origin vertex of parent (ppCollision vertex)
+  SmartRef<LHCb::MCVertex> p_orgvertx = new LHCb::MCVertex();
+  p_orgvertx->setType( LHCb::MCVertex::ppCollision );
+
+  // Define end vertices of parent
+  const Gaudi::XYZPoint            p_endvrtx{2.8, -1.5, 1.3};
+  SmartRefVector<LHCb::MCParticle> prods_endvertx;
+  prods_endvertx.push_back( &p1 );
+  prods_endvertx.push_back( &p2 );
+  SmartRef<LHCb::MCVertex> p_endvertx = new LHCb::MCVertex();
+  p_endvertx->setPosition( p_endvrtx );
+  p_endvertx->setTime( 0.05 );
+  p_endvertx->setType( LHCb::MCVertex::DecayVertex );
+  p_endvertx->setProducts( prods_endvertx ); // products in this vertex (particle 1 and 2)
+  SmartRefVector<LHCb::MCVertex> p_endvertxs;
+  p_endvertxs.push_back( p_endvertx );
+
+  // set properties of p
+  const LHCb::ParticleID p_id = LHCb::ParticleID( parentid ); // Jpsi
+  p.setParticleID( p_id );
+  p.setMomentum( p1_4vec + p2_4vec );
+  p.setOriginVertex( p_orgvertx );
+  p.setEndVertices( p_endvertxs );
+
+  /*
+  this->info() << p << endmsg;
+  for (const auto& mcv: p.endVertices()){
+          this->info() << *mcv << endmsg;
+          for (const auto& mcd: (*mcv).products()){this->info() << *mcd << endmsg;}
+  }
+  */
+}
+
+DECLARE_COMPONENT_WITH_ID( FunTuple<LHCb::FTuple::MCPARTICLES>, "FunTuple" )
diff --git a/Phys/FunTuple/src/MakeData.cpp b/Phys/FunTuple/src/MakeData.cpp
new file mode 100755
index 000000000..d05741bd3
--- /dev/null
+++ b/Phys/FunTuple/src/MakeData.cpp
@@ -0,0 +1,233 @@
+/*****************************************************************************\
+* (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.                                       *
+\*****************************************************************************/
+
+// Gaudi
+#include "GaudiAlg/Producer.h"
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/Point3DTypes.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/Vector3DTypes.h"
+#include "GaudiKernel/Vector4DTypes.h"
+
+// Event
+#include "Event/MCParticle.h"
+#include "Event/MCVertex.h"
+
+// Kernel
+#include "Kernel/IParticlePropertySvc.h"
+#include "Kernel/ParticleID.h"
+#include "Kernel/ParticleProperty.h"
+
+// LoKi
+#include "LoKi/IMCDecay.h"
+#include "LoKi/PrintMCDecay.h"
+#include "LoKi/Trees.h"
+
+class MakeData : public Gaudi::Functional::Producer<std::vector<LHCb::MCParticle>()> {
+public:
+  MakeData( const std::string& name, ISvcLocator* svcLoc )
+      : Producer( name, svcLoc, {"output_location", {"MyParticle"}} ) {}
+  std::vector<LHCb::MCParticle> operator()() const override;
+
+protected:
+  void makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2, const int& parentid,
+                           const int& prod1id, const int& prod2id, MsgStream& log ) const;
+  void makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2, const int& parentid ) const;
+  void checkDecay( Decays::IMCDecay::Finder& finder, LHCb::MCParticle::ConstVector& mcparticles, MsgStream& log ) const;
+
+private:
+  Gaudi::Property<std::string> m_headDecay{
+      this, "decay_descriptor", "[B_s0 => (J/psi(1S) => mu+ mu- ) ^( phi(1020) => K+ K-)]CC", "Decay descriptor"};
+  ServiceHandle<LHCb::IParticlePropertySvc> m_particlePropertySvc{this, "ParticleProperty",
+                                                                  "LHCb::ParticlePropertySvc"};
+  ToolHandle<Decays::IMCDecay>              m_mcdecaytool = {this, "MCDecay", "LoKi::MCDecay"};
+};
+
+DECLARE_COMPONENT( MakeData )
+
+std::vector<LHCb::MCParticle> MakeData::operator()() const {
+  MsgStream log = info();
+  MsgStream err = error();
+  log << "executing MakeData" << endmsg;
+
+  // parse/decode the decay descriptor
+  const Decays::IMCDecay::Tree mcdecayTree = m_mcdecaytool->tree( m_headDecay );
+  if ( !mcdecayTree ) { err << "Could not find mcdecayTree for " + m_headDecay << endmsg; }
+  log << "MC decay descriptor parsed is " << mcdecayTree << endmsg;
+
+  // Define and fill container to hold the Jpsi MCParticles (only one Jpsi made) which decays to mu+ mu-
+  std::vector<LHCb::MCParticle> mcparticles;
+  LHCb::MCParticle              p1   = LHCb::MCParticle();
+  LHCb::MCParticle              p1_a = LHCb::MCParticle();
+  LHCb::MCParticle              p1_b = LHCb::MCParticle();
+  LHCb::MCParticle              p2   = LHCb::MCParticle();
+  LHCb::MCParticle              p2_a = LHCb::MCParticle();
+  LHCb::MCParticle              p2_b = LHCb::MCParticle();
+  LHCb::MCParticle              p    = LHCb::MCParticle();
+
+  makeMCTwoBodyDecay( p1, p1_a, p1_b, 443, 13, -13, err );   // Jpis -> mu- mu+
+  makeMCTwoBodyDecay( p2, p2_a, p2_b, 333, 321, -321, err ); // phi -> K+ K-
+  makeMCTwoBodyDecay( p, p1, p2, 531 );                      // Bs -> Jpsi phi
+
+  mcparticles.push_back( p1 );
+  mcparticles.push_back( p1_a );
+  mcparticles.push_back( p1_b );
+  mcparticles.push_back( p2 );
+  mcparticles.push_back( p2_a );
+  mcparticles.push_back( p2_b );
+  mcparticles.push_back( p );
+
+  LHCb::MCParticle::ConstVector mcparticlescheck{&mcparticles[0]};
+  /*
+  //Check all the particles are there
+  for (const auto& mcp: mcparticlescheck) {
+          log << *mcp << endmsg;
+          for (const auto& mcv: (*mcp).endVertices()){
+                  //log << *mcv << endmsg;
+                  for (const auto& mcp1: (*mcv).products()){
+                          log << *mcp1 << endmsg;
+                          for (const auto& mcv1: (*mcp1).endVertices()){
+                                  //log << *mcv1 << endmsg;
+                                  for (const auto& mcp2: (*mcv1).products()){log << *mcp2 << endmsg;}
+                          }
+                  }
+          }
+  }
+  */
+
+  // create the decay finder
+  Decays::IMCDecay::Finder finder( mcdecayTree );
+  if ( !finder ) { err << "Unable to create decay finder'" << endmsg; }
+
+  ////check that the finder can find the decay using the descriptor
+  checkDecay( finder, mcparticlescheck, log );
+
+  return mcparticles;
+}
+
+void MakeData::makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2, const int& parentid,
+                                   const int& prod1id, const int& prod2id, MsgStream& log ) const {
+  using std::sqrt;
+
+  // particle id of products
+  const LHCb::ParticleID p1_id = LHCb::ParticleID( prod1id ); // mu+
+  const LHCb::ParticleID p2_id = LHCb::ParticleID( prod2id ); // mu-
+  p1.setParticleID( p1_id );
+  p2.setParticleID( p2_id );
+  // four momenta of products
+  const LHCb::ParticleProperty* p1_prop = m_particlePropertySvc->find( p1_id );
+  const LHCb::ParticleProperty* p2_prop = m_particlePropertySvc->find( p2_id );
+  if ( !p1_prop || !p2_prop ) { log << "Could not find either p1_prop or p2_prop" << endmsg; }
+  const double               p1_mass = p1_prop->mass();
+  const double               p2_mass = p2_prop->mass();
+  const Gaudi::XYZVector     p1_3vec{10.2, 10.9, 30.2};
+  const Gaudi::XYZVector     p2_3vec{20.5, 20.5, 60.6};
+  double                     p1_E    = sqrt( p1_3vec.Mag2() + p1_mass * p1_mass );
+  double                     p2_E    = sqrt( p2_3vec.Mag2() + p2_mass * p2_mass );
+  const Gaudi::LorentzVector p1_4vec = Gaudi::LorentzVector( p1_3vec.X(), p1_3vec.Y(), p1_3vec.Z(), p1_E );
+  const Gaudi::LorentzVector p2_4vec = Gaudi::LorentzVector( p2_3vec.X(), p2_3vec.Y(), p2_3vec.Z(), p2_E );
+  p1.setMomentum( p1_4vec );
+  p2.setMomentum( p2_4vec );
+
+  // Define origin vertex of parent (DecayVertex)
+  SmartRef<LHCb::MCVertex> p_orgvertx = new LHCb::MCVertex();
+  const Gaudi::XYZPoint    p_orgvrtx{2.8, -1.5, 1.3};
+  p_orgvertx->setPosition( p_orgvrtx );
+  p_orgvertx->setTime( 0.05 );
+  p_orgvertx->setType( LHCb::MCVertex::DecayVertex );
+
+  // Define end vertices of parent
+  const Gaudi::XYZPoint            p_endvrtx{6.8, -4.5, 3.3};
+  SmartRefVector<LHCb::MCParticle> prods_endvertx;
+  prods_endvertx.push_back( &p1 );
+  prods_endvertx.push_back( &p2 );
+  SmartRef<LHCb::MCVertex> p_endvertx = new LHCb::MCVertex();
+  p_endvertx->setPosition( p_endvrtx );
+  p_endvertx->setTime( 0.13 );
+  p_endvertx->setType( LHCb::MCVertex::DecayVertex );
+  p_endvertx->setProducts( prods_endvertx ); // products in this vertex (particle 1 and 2)
+  SmartRefVector<LHCb::MCVertex> p_endvertxs;
+  p_endvertxs.push_back( p_endvertx );
+
+  // set properties of parent
+  const LHCb::ParticleID p_id = LHCb::ParticleID( parentid ); // Jpsi
+  p.setParticleID( p_id );
+  p.setMomentum( p1_4vec + p2_4vec );
+  p.setOriginVertex( p_orgvertx );
+  p.setEndVertices( p_endvertxs );
+
+  // log << p << endmsg;
+  // for (const auto& mcv: p.endVertices()){
+  //	log << *mcv << endmsg;
+  //	for (const auto& mcd: (*mcv).products()){log << *mcd << endmsg;}
+  //}
+}
+
+void MakeData::makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2,
+                                   const int& parentid ) const {
+  // get 4-momenta
+  const Gaudi::LorentzVector p1_4vec = p1.momentum();
+  const Gaudi::LorentzVector p2_4vec = p2.momentum();
+
+  // Define origin vertex of parent (ppCollision vertex)
+  SmartRef<LHCb::MCVertex> p_orgvertx = new LHCb::MCVertex();
+  p_orgvertx->setType( LHCb::MCVertex::ppCollision );
+
+  // Define end vertices of parent
+  const Gaudi::XYZPoint            p_endvrtx{2.8, -1.5, 1.3};
+  SmartRefVector<LHCb::MCParticle> prods_endvertx;
+  prods_endvertx.push_back( &p1 );
+  prods_endvertx.push_back( &p2 );
+  SmartRef<LHCb::MCVertex> p_endvertx = new LHCb::MCVertex();
+  p_endvertx->setPosition( p_endvrtx );
+  p_endvertx->setTime( 0.05 );
+  p_endvertx->setType( LHCb::MCVertex::DecayVertex );
+  p_endvertx->setProducts( prods_endvertx ); // products in this vertex (particle 1 and 2)
+  SmartRefVector<LHCb::MCVertex> p_endvertxs;
+  p_endvertxs.push_back( p_endvertx );
+
+  // set properties of p
+  const LHCb::ParticleID p_id = LHCb::ParticleID( parentid ); // Jpsi
+  p.setParticleID( p_id );
+  p.setMomentum( p1_4vec + p2_4vec );
+  p.setOriginVertex( p_orgvertx );
+  p.setEndVertices( p_endvertxs );
+}
+
+void MakeData::checkDecay( Decays::IMCDecay::Finder& finder, LHCb::MCParticle::ConstVector& mcparticles,
+                           MsgStream& log ) const {
+  // make the output container
+  typedef LHCb::MCParticle::ConstVector OUTPUT;
+  OUTPUT                                output;
+
+  // fill the container
+  finder.findDecay( mcparticles.begin(), mcparticles.end(), output );
+  log << " found #" << output.size() << " decays" << endmsg;
+
+  // print the marked particle in the container
+  for ( const auto& p : output ) {
+    log << *p << endmsg;
+    // for (const auto& mcv: (*p).endVertices()){
+    //	log << *mcv << endmsg;
+    //	for (const auto& mcd: (*mcv).products()){log << *mcd << endmsg;}
+    //}
+  }
+
+  //// print decay: Some reason the loKi services cannot import ParticlePropertyService properly
+  // for ( OUTPUT::const_iterator idec = output.begin() ; output.end() != idec ; ++idec )
+  //{
+  //	const LHCb::MCParticle* dec = *idec ;
+  //	if ( 0 == dec ) { continue ; }
+  //	log << std::endl << " " << (idec-output.begin()+1) << " \t" ;
+  //	LoKi::PrintMC::printDecay(dec,log,true);
+  //}
+  // log << endmsg ;
+}
diff --git a/Phys/FunTuple/src/ParticleTupleProp.cpp b/Phys/FunTuple/src/ParticleTupleProp.cpp
new file mode 100755
index 000000000..e90a7ff68
--- /dev/null
+++ b/Phys/FunTuple/src/ParticleTupleProp.cpp
@@ -0,0 +1,47 @@
+/*****************************************************************************\
+* (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.                                       *
+\*****************************************************************************/
+
+#include "ParticleTupleProp.h"
+
+bool ParticleTupleProp::checks() {
+  if ( m_branchname.empty() ) { return ErrorMsg( "Error branch name is empty" ); }
+
+  if ( m_decaydescriptor.empty() ) { return ErrorMsg( "Error decay descriptor is empty" ); }
+
+  int count( 0 );
+  for ( const auto& mtp : m_tupleprops ) {
+    if ( mtp.Func.empty() ) { return ErrorMsg( "Functor name is empty at " + std::to_string( count ) ); }
+    if ( mtp.Func_branchname.empty() ) {
+      return ErrorMsg( "Functor branchname is empty at " + std::to_string( count ) );
+    }
+    if ( mtp.Func_returntype.empty() ) {
+      return ErrorMsg( "Functor return type is empty at " + std::to_string( count ) );
+    }
+    count++;
+  }
+  return true;
+}
+
+void ParticleTupleProp::setFunctorProps( std::vector<std::string>& functors, std::vector<std::string>& funcbranchnames,
+                                         std::vector<std::string>& funcreturntypes ) {
+  for ( unsigned int i = 0; i < functors.size(); i++ ) {
+    ParticleProp::FunctorProp tupleprop;
+    tupleprop.Func            = functors[i];
+    tupleprop.Func_branchname = funcbranchnames[i];
+    tupleprop.Func_returntype = funcreturntypes[i];
+    m_tupleprops.push_back( tupleprop );
+  }
+}
+
+bool ParticleTupleProp::ErrorMsg( std::string stg ) { // TODO: better exception handling
+  std::cout << stg << std::endl;
+  return false;
+}
diff --git a/Phys/FunTuple/src/ParticleTupleProp.h b/Phys/FunTuple/src/ParticleTupleProp.h
new file mode 100755
index 000000000..44e72f5ad
--- /dev/null
+++ b/Phys/FunTuple/src/ParticleTupleProp.h
@@ -0,0 +1,57 @@
+/*****************************************************************************\
+* (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.                                       *
+\*****************************************************************************/
+
+#pragma once
+
+// std lib
+#include <iostream>
+#include <vector>
+
+// LoKi
+#include "LoKi/IDecay.h"
+#include "LoKi/IHybridFactory.h"
+#include "LoKi/IMCDecay.h"
+#include "LoKi/IMCHybridFactory.h"
+#include "LoKi/Trees.h"
+
+namespace ParticleProp {
+  typedef std::vector<std::vector<std::string>> TUPLELIST;
+  // define a struct to hold functor tupling properties
+  struct FunctorProp {
+    std::string Func{""};            // Functor name
+    std::string Func_branchname{""}; // Functor TBranch name
+    std::string Func_returntype{""}; // Functor return type
+  };
+} // namespace ParticleProp
+
+class ParticleTupleProp {
+public:
+  ParticleTupleProp() {}
+  // set functions
+  void setBranchName( std::string branchname ) { m_branchname = branchname; }
+  void setDecayDescriptor( std::string decaydescriptor ) { m_decaydescriptor = decaydescriptor; }
+  void setFunctorProps( std::vector<ParticleProp::FunctorProp> tupleprops ) { m_tupleprops = tupleprops; }
+  void setFunctorProps( std::vector<std::string>& functors, std::vector<std::string>& funcbranchnames,
+                        std::vector<std::string>& funcreturntypes );
+  // get functions
+  const std::string&                            BranchName() const { return m_branchname; }
+  const std::string&                            DecayDescriptor() const { return m_decaydescriptor; }
+  const std::vector<ParticleProp::FunctorProp>& FunctorProps() const { return m_tupleprops; }
+  // check and validate
+  bool checks();
+
+protected:
+  bool ErrorMsg( std::string stg ); // TODO: Better exception handling
+private:
+  std::string                            m_branchname{""};
+  std::string                            m_decaydescriptor{""};
+  std::vector<ParticleProp::FunctorProp> m_tupleprops;
+};
diff --git a/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt b/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt
new file mode 100644
index 000000000..2822653fb
--- /dev/null
+++ b/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt
@@ -0,0 +1,35 @@
+<?xml version="1.0" ?><!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+    (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.
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+  <argument name="program"><text>gaudirun.py</text></argument>
+  <argument name="args"><set><text>options/FunTuple_opts.py</text></set></argument>
+  <argument name="validator"><text>
+import sys, os, glob
+import ROOT as r
+from ROOT import TFile
+
+try:
+    ntuple = 'FunTuple.root'
+    f = TFile.Open(ntuple)
+    if f.IsZombie():
+        raise AttributeError('Output ROOT file is Zombie')
+    print('-- FunTuple QMTest -- : found output NTuple')
+    f.Close()
+    os.system('rm %s' %ntuple)
+except IOError as err:
+    raise IOError('Impossible to open file: ', err)
+except:
+    print('Unexpected error while opening file: ', sys.exc_info()[0])
+  </text></argument>
+
+</extension>
+
diff --git a/Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py b/Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py
new file mode 100755
index 000000000..d7e049153
--- /dev/null
+++ b/Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env gaudirun.py
+###############################################################################
+# (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.                                       #
+###############################################################################
+
+
+######### Define helpful classes and functions
+class ParticleTupleProp:
+    def __init__(self, branch_name, decay_descriptor, particle_code):
+        """
+        branch_name     : Branch name of particle
+        decay_descriptor: Decay descriptor
+        particle_code   : List of tuples. Each tuple contains strings defining
+                         (Functorcode, corresponding branch name, C++ return type that could handle precision, description).
+        """
+        self.branch_name = branch_name
+        self.decay_descriptor = decay_descriptor
+        self.particle_code = particle_code
+
+    def to_dict(self):
+        return {self.branch_name: {self.decay_descriptor: self.particle_code}}
+
+
+def convert_to_parsable_objs(particle_tuple_props):
+    """
+    particle_tuple_props: List of ParticleTupleProp
+    """
+
+    def get_lists(ptps, i):
+        f_list = []
+        for ptp in ptps:
+            f_list += [[pc[i] for pc in ptp.particle_code]]
+
+        return f_list
+
+    branch_list = [ptp.branch_name for ptp in particle_tuple_props]
+    discrp_list = [ptp.decay_descriptor for ptp in particle_tuple_props]
+    func_list = get_lists(particle_tuple_props, 0)
+    func_bnamelist = get_lists(particle_tuple_props, 1)
+    func_rtypelist = get_lists(particle_tuple_props, 2)
+
+    return branch_list, discrp_list, func_list, func_bnamelist, func_rtypelist
+
+
+#########
+
+from Gaudi.Configuration import *
+from Configurables import LHCbApp
+from Configurables import LHCb__ParticlePropertySvc
+LHCb__ParticlePropertySvc(
+)  #To turn on verbose set LHCb__ParticlePropertySvc(OutputLevel=1)
+from Configurables import CondDB
+CondDB(Upgrade=True)
+
+app = LHCbApp()
+app.DataType = "Upgrade"
+app.DDDBtag = "dddb-20171126"
+app.CondDBtag = "sim-20171127-vc-md100"
+app.Simulation = True
+ApplicationMgr().EvtMax = 5
+ApplicationMgr().EvtSel = "NONE"
+
+#Make MCParticles (just one Bs): Turns out TES is not clever enough to take ownership 'recursively' (set makeCustomData = True if running this algorithm)
+from Configurables import MakeData
+algMakeData = MakeData(
+    name="Alg_MakeData",
+    decay_descriptor=
+    "[B_s0 => (J/psi(1S) => mu+ mu- ) ( phi(1020) => K+ K-)]CC",
+    output_location="MCParticle")
+
+#Do we need a custom gaudi property like ParticleTupleProp?
+#Configure Particles Bs
+ParticleBs = ParticleTupleProp(
+    branch_name="Bs",
+    decay_descriptor=
+    "[B_s0 => (J/psi(1S) => mu+ mu- ) ( phi(1020) => K+ K-)]CC",
+    #List of tuple, each tuple (functor, branch name, precision (TODO), description).
+    particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
+                   ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
+#Configure Particles Phi
+ParticlePhi = ParticleTupleProp(
+    branch_name="Phi",
+    decay_descriptor=
+    "[B_s0 => (J/psi(1S) => mu+ mu- ) ^( phi(1020) => K+ K-)]CC",
+    particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
+                   ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
+#Do not need to do this if a custom gaudi property is implemented
+branchlist, decaydiscrpList, funcList, funcBranchNameList, funcRetunTypeList = convert_to_parsable_objs(
+    [ParticleBs, ParticlePhi])
+
+from Configurables import FunTuple
+algtupledata = FunTuple(
+    name="FunTuple",
+    tree_name="DecayTree",
+    branch_names=branchlist,
+    is_MC=True,  #Since passing MCParticles
+    decay_descriptors=decaydiscrpList,
+    functors=funcList,
+    functor_branch_names=funcBranchNameList,
+    functor_return_types=funcRetunTypeList,
+    make_custom_data=
+    True,  #Only set to True since running over MakeData algo (produces its own Bs->Jpsiphi MC decays).
+    input_location="MCParticle",
+    NTupleLUN='tuple')
+
+ApplicationMgr().TopAlg = [algMakeData, algtupledata]
+
+#Tuple props
+outputname = "FunTuple.root"
+NTupleSvc().Output = [
+    "{} DATAFILE='{}' TYP='ROOT' OPT='NEW'".format(algtupledata.NTupleLUN,
+                                                   outputname)
+]
+ApplicationMgr().ExtSvc.append(NTupleSvc())
+ApplicationMgr().HistogramPersistency = "ROOT"
+# Set the compression level for the ROOT tuple file
+from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
+RFileCnv('RFileCnv').GlobalCompression = "LZMA:6"
-- 
GitLab


From 7fc979e7bf8c808bfee9009565ffde0d8d84f46e Mon Sep 17 00:00:00 2001
From: amathad <Abhijit Mathad amathad@cern.ch>
Date: Tue, 2 Mar 2021 18:30:48 +0100
Subject: [PATCH 2/6] TuplMinor edits to options file

---
 Phys/FunTuple/CMakeLists.txt                  |  3 +-
 .../{FunTuple_opts.py => example-FunTuple.py} | 46 +++++++++++--------
 .../qmtest/algorithms.qms/test-funtuple.qmt   |  2 +-
 .../{FunTuple_opts.py => example-FunTuple.py} | 46 +++++++++++--------
 4 files changed, 56 insertions(+), 41 deletions(-)
 rename Phys/FunTuple/options/{FunTuple_opts.py => example-FunTuple.py} (88%)
 rename Phys/FunTuple/tests/qmtest/options/{FunTuple_opts.py => example-FunTuple.py} (88%)

diff --git a/Phys/FunTuple/CMakeLists.txt b/Phys/FunTuple/CMakeLists.txt
index c87a27354..cf00de75b 100644
--- a/Phys/FunTuple/CMakeLists.txt
+++ b/Phys/FunTuple/CMakeLists.txt
@@ -13,8 +13,7 @@ gaudi_subdir(FunTuple)
 
 gaudi_depends_on_subdirs(Phys/DaVinciKernel
                          Phys/DaVinciMCKernel
-			 			 Kernel/LHCbMath
-                         Phys/LoKi
+						 Kernel/LHCbMath
                          Phys/LoKiMC
                          Phys/LoKiAlgoMC
                          Phys/LoKiArrayFunctors
diff --git a/Phys/FunTuple/options/FunTuple_opts.py b/Phys/FunTuple/options/example-FunTuple.py
similarity index 88%
rename from Phys/FunTuple/options/FunTuple_opts.py
rename to Phys/FunTuple/options/example-FunTuple.py
index d7e049153..6036195d1 100755
--- a/Phys/FunTuple/options/FunTuple_opts.py
+++ b/Phys/FunTuple/options/example-FunTuple.py
@@ -9,9 +9,28 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 ###############################################################################
+"""
+Example of tupling with the FunTuple algorithm
+"""
 
+from Configurables import LHCbApp
+from Gaudi.Configuration import ApplicationMgr, NTupleSvc
+from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
+from Configurables import CondDB
+from Configurables import MakeData
+from Configurables import FunTuple
 
-######### Define helpful classes and functions
+
+""" Define helpful class and function
+
+class ParticleTupleProp: 
+    A class that one can configure with branch_name, decay descriptor and functors. 
+
+Function convert_to_parsable_objs: 
+    Since ParticleTupleProp cannot currently be passed as Gaudi::Property, define 
+    this function to turn member variables into parsable_objs for FunTuple
+
+"""
 class ParticleTupleProp:
     def __init__(self, branch_name, decay_descriptor, particle_code):
         """
@@ -48,17 +67,7 @@ def convert_to_parsable_objs(particle_tuple_props):
 
     return branch_list, discrp_list, func_list, func_bnamelist, func_rtypelist
 
-
-#########
-
-from Gaudi.Configuration import *
-from Configurables import LHCbApp
-from Configurables import LHCb__ParticlePropertySvc
-LHCb__ParticlePropertySvc(
-)  #To turn on verbose set LHCb__ParticlePropertySvc(OutputLevel=1)
-from Configurables import CondDB
-CondDB(Upgrade=True)
-
+#Configure LHCb application
 app = LHCbApp()
 app.DataType = "Upgrade"
 app.DDDBtag = "dddb-20171126"
@@ -66,17 +75,16 @@ app.CondDBtag = "sim-20171127-vc-md100"
 app.Simulation = True
 ApplicationMgr().EvtMax = 5
 ApplicationMgr().EvtSel = "NONE"
+CondDB(Upgrade=True)
 
 #Make MCParticles (just one Bs): Turns out TES is not clever enough to take ownership 'recursively' (set makeCustomData = True if running this algorithm)
-from Configurables import MakeData
 algMakeData = MakeData(
     name="Alg_MakeData",
     decay_descriptor=
     "[B_s0 => (J/psi(1S) => mu+ mu- ) ( phi(1020) => K+ K-)]CC",
     output_location="MCParticle")
 
-#Do we need a custom gaudi property like ParticleTupleProp?
-#Configure Particles Bs
+#Configure Particles Bs and phi
 ParticleBs = ParticleTupleProp(
     branch_name="Bs",
     decay_descriptor=
@@ -84,18 +92,18 @@ ParticleBs = ParticleTupleProp(
     #List of tuple, each tuple (functor, branch name, precision (TODO), description).
     particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
                    ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
-#Configure Particles Phi
 ParticlePhi = ParticleTupleProp(
     branch_name="Phi",
     decay_descriptor=
     "[B_s0 => (J/psi(1S) => mu+ mu- ) ^( phi(1020) => K+ K-)]CC",
     particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
                    ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
+
 #Do not need to do this if a custom gaudi property is implemented
 branchlist, decaydiscrpList, funcList, funcBranchNameList, funcRetunTypeList = convert_to_parsable_objs(
     [ParticleBs, ParticlePhi])
 
-from Configurables import FunTuple
+#Configure FunTuple algorithm
 algtupledata = FunTuple(
     name="FunTuple",
     tree_name="DecayTree",
@@ -110,9 +118,10 @@ algtupledata = FunTuple(
     input_location="MCParticle",
     NTupleLUN='tuple')
 
+#add the algorithms to the sequence
 ApplicationMgr().TopAlg = [algMakeData, algtupledata]
 
-#Tuple props
+#set Tuple properties
 outputname = "FunTuple.root"
 NTupleSvc().Output = [
     "{} DATAFILE='{}' TYP='ROOT' OPT='NEW'".format(algtupledata.NTupleLUN,
@@ -121,5 +130,4 @@ NTupleSvc().Output = [
 ApplicationMgr().ExtSvc.append(NTupleSvc())
 ApplicationMgr().HistogramPersistency = "ROOT"
 # Set the compression level for the ROOT tuple file
-from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
 RFileCnv('RFileCnv').GlobalCompression = "LZMA:6"
diff --git a/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt b/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt
index 2822653fb..10e103792 100644
--- a/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt
+++ b/Phys/FunTuple/tests/qmtest/algorithms.qms/test-funtuple.qmt
@@ -11,7 +11,7 @@
 -->
 <extension class="GaudiTest.GaudiExeTest" kind="test">
   <argument name="program"><text>gaudirun.py</text></argument>
-  <argument name="args"><set><text>options/FunTuple_opts.py</text></set></argument>
+  <argument name="args"><set><text>options/example-FunTuple.py</text></set></argument>
   <argument name="validator"><text>
 import sys, os, glob
 import ROOT as r
diff --git a/Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py b/Phys/FunTuple/tests/qmtest/options/example-FunTuple.py
similarity index 88%
rename from Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py
rename to Phys/FunTuple/tests/qmtest/options/example-FunTuple.py
index d7e049153..6036195d1 100755
--- a/Phys/FunTuple/tests/qmtest/options/FunTuple_opts.py
+++ b/Phys/FunTuple/tests/qmtest/options/example-FunTuple.py
@@ -9,9 +9,28 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 ###############################################################################
+"""
+Example of tupling with the FunTuple algorithm
+"""
 
+from Configurables import LHCbApp
+from Gaudi.Configuration import ApplicationMgr, NTupleSvc
+from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
+from Configurables import CondDB
+from Configurables import MakeData
+from Configurables import FunTuple
 
-######### Define helpful classes and functions
+
+""" Define helpful class and function
+
+class ParticleTupleProp: 
+    A class that one can configure with branch_name, decay descriptor and functors. 
+
+Function convert_to_parsable_objs: 
+    Since ParticleTupleProp cannot currently be passed as Gaudi::Property, define 
+    this function to turn member variables into parsable_objs for FunTuple
+
+"""
 class ParticleTupleProp:
     def __init__(self, branch_name, decay_descriptor, particle_code):
         """
@@ -48,17 +67,7 @@ def convert_to_parsable_objs(particle_tuple_props):
 
     return branch_list, discrp_list, func_list, func_bnamelist, func_rtypelist
 
-
-#########
-
-from Gaudi.Configuration import *
-from Configurables import LHCbApp
-from Configurables import LHCb__ParticlePropertySvc
-LHCb__ParticlePropertySvc(
-)  #To turn on verbose set LHCb__ParticlePropertySvc(OutputLevel=1)
-from Configurables import CondDB
-CondDB(Upgrade=True)
-
+#Configure LHCb application
 app = LHCbApp()
 app.DataType = "Upgrade"
 app.DDDBtag = "dddb-20171126"
@@ -66,17 +75,16 @@ app.CondDBtag = "sim-20171127-vc-md100"
 app.Simulation = True
 ApplicationMgr().EvtMax = 5
 ApplicationMgr().EvtSel = "NONE"
+CondDB(Upgrade=True)
 
 #Make MCParticles (just one Bs): Turns out TES is not clever enough to take ownership 'recursively' (set makeCustomData = True if running this algorithm)
-from Configurables import MakeData
 algMakeData = MakeData(
     name="Alg_MakeData",
     decay_descriptor=
     "[B_s0 => (J/psi(1S) => mu+ mu- ) ( phi(1020) => K+ K-)]CC",
     output_location="MCParticle")
 
-#Do we need a custom gaudi property like ParticleTupleProp?
-#Configure Particles Bs
+#Configure Particles Bs and phi
 ParticleBs = ParticleTupleProp(
     branch_name="Bs",
     decay_descriptor=
@@ -84,18 +92,18 @@ ParticleBs = ParticleTupleProp(
     #List of tuple, each tuple (functor, branch name, precision (TODO), description).
     particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
                    ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
-#Configure Particles Phi
 ParticlePhi = ParticleTupleProp(
     branch_name="Phi",
     decay_descriptor=
     "[B_s0 => (J/psi(1S) => mu+ mu- ) ^( phi(1020) => K+ K-)]CC",
     particle_code=[('MCP', 'TRUEP', 'double', 'True Moment'),
                    ('MCPT', 'TRUEPT', 'double', 'True Transverse moment')])
+
 #Do not need to do this if a custom gaudi property is implemented
 branchlist, decaydiscrpList, funcList, funcBranchNameList, funcRetunTypeList = convert_to_parsable_objs(
     [ParticleBs, ParticlePhi])
 
-from Configurables import FunTuple
+#Configure FunTuple algorithm
 algtupledata = FunTuple(
     name="FunTuple",
     tree_name="DecayTree",
@@ -110,9 +118,10 @@ algtupledata = FunTuple(
     input_location="MCParticle",
     NTupleLUN='tuple')
 
+#add the algorithms to the sequence
 ApplicationMgr().TopAlg = [algMakeData, algtupledata]
 
-#Tuple props
+#set Tuple properties
 outputname = "FunTuple.root"
 NTupleSvc().Output = [
     "{} DATAFILE='{}' TYP='ROOT' OPT='NEW'".format(algtupledata.NTupleLUN,
@@ -121,5 +130,4 @@ NTupleSvc().Output = [
 ApplicationMgr().ExtSvc.append(NTupleSvc())
 ApplicationMgr().HistogramPersistency = "ROOT"
 # Set the compression level for the ROOT tuple file
-from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
 RFileCnv('RFileCnv').GlobalCompression = "LZMA:6"
-- 
GitLab


From 5ba669a05b3ca54a801c5ea68e65768799f54381 Mon Sep 17 00:00:00 2001
From: Gitlab CI <noreply@cern.ch>
Date: Tue, 2 Mar 2021 17:31:36 +0000
Subject: [PATCH 3/6] Fixed formatting

patch generated by https://gitlab.cern.ch/lhcb/Analysis/-/jobs/12437619
---
 Phys/FunTuple/options/example-FunTuple.py           | 13 +++++++------
 .../tests/qmtest/options/example-FunTuple.py        | 13 +++++++------
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/Phys/FunTuple/options/example-FunTuple.py b/Phys/FunTuple/options/example-FunTuple.py
index 6036195d1..e32545238 100755
--- a/Phys/FunTuple/options/example-FunTuple.py
+++ b/Phys/FunTuple/options/example-FunTuple.py
@@ -19,18 +19,18 @@ from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
 from Configurables import CondDB
 from Configurables import MakeData
 from Configurables import FunTuple
-
-
 """ Define helpful class and function
 
-class ParticleTupleProp: 
-    A class that one can configure with branch_name, decay descriptor and functors. 
+class ParticleTupleProp:
+    A class that one can configure with branch_name, decay descriptor and functors.
 
-Function convert_to_parsable_objs: 
-    Since ParticleTupleProp cannot currently be passed as Gaudi::Property, define 
+Function convert_to_parsable_objs:
+    Since ParticleTupleProp cannot currently be passed as Gaudi::Property, define
     this function to turn member variables into parsable_objs for FunTuple
 
 """
+
+
 class ParticleTupleProp:
     def __init__(self, branch_name, decay_descriptor, particle_code):
         """
@@ -67,6 +67,7 @@ def convert_to_parsable_objs(particle_tuple_props):
 
     return branch_list, discrp_list, func_list, func_bnamelist, func_rtypelist
 
+
 #Configure LHCb application
 app = LHCbApp()
 app.DataType = "Upgrade"
diff --git a/Phys/FunTuple/tests/qmtest/options/example-FunTuple.py b/Phys/FunTuple/tests/qmtest/options/example-FunTuple.py
index 6036195d1..e32545238 100755
--- a/Phys/FunTuple/tests/qmtest/options/example-FunTuple.py
+++ b/Phys/FunTuple/tests/qmtest/options/example-FunTuple.py
@@ -19,18 +19,18 @@ from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
 from Configurables import CondDB
 from Configurables import MakeData
 from Configurables import FunTuple
-
-
 """ Define helpful class and function
 
-class ParticleTupleProp: 
-    A class that one can configure with branch_name, decay descriptor and functors. 
+class ParticleTupleProp:
+    A class that one can configure with branch_name, decay descriptor and functors.
 
-Function convert_to_parsable_objs: 
-    Since ParticleTupleProp cannot currently be passed as Gaudi::Property, define 
+Function convert_to_parsable_objs:
+    Since ParticleTupleProp cannot currently be passed as Gaudi::Property, define
     this function to turn member variables into parsable_objs for FunTuple
 
 """
+
+
 class ParticleTupleProp:
     def __init__(self, branch_name, decay_descriptor, particle_code):
         """
@@ -67,6 +67,7 @@ def convert_to_parsable_objs(particle_tuple_props):
 
     return branch_list, discrp_list, func_list, func_bnamelist, func_rtypelist
 
+
 #Configure LHCb application
 app = LHCbApp()
 app.DataType = "Upgrade"
-- 
GitLab


From f4fd0c49a37ec247fbea876d3972524c5494339f Mon Sep 17 00:00:00 2001
From: amathad <Abhijit Mathad amathad@cern.ch>
Date: Wed, 3 Mar 2021 12:10:04 +0100
Subject: [PATCH 4/6] Remove Gaudi install headers for CombKernel

---
 Phys/FunTuple/CMakeLists.txt | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/Phys/FunTuple/CMakeLists.txt b/Phys/FunTuple/CMakeLists.txt
index cf00de75b..d9effa579 100644
--- a/Phys/FunTuple/CMakeLists.txt
+++ b/Phys/FunTuple/CMakeLists.txt
@@ -11,20 +11,18 @@
 
 gaudi_subdir(FunTuple)
 
-gaudi_depends_on_subdirs(Phys/DaVinciKernel
-                         Phys/DaVinciMCKernel
-						 Kernel/LHCbMath
-                         Phys/LoKiMC
-                         Phys/LoKiAlgoMC
-                         Phys/LoKiArrayFunctors
-                         Phys/LoKiPhys
-                         )
+gaudi_depends_on_subdirs(Phys/DaVinciKernel Phys/DaVinciMCKernel Kernel/LHCbMath
+                         	Phys/LoKiMC
+                         	Phys/LoKiAlgoMC
+                         	Phys/LoKiArrayFunctors
+                         	Phys/LoKiPhys
+                     	)
 
 find_package(Boost)
 find_package(ROOT COMPONENTS RIO Tree)
 include_directories(SYSTEM ${ROOT_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
 
-gaudi_install_headers(CombKernel)
+#gaudi_install_headers(CombKernel)
 
 gaudi_add_module(FunTuple
                  src/*.cpp
@@ -32,4 +30,4 @@ gaudi_add_module(FunTuple
 
 gaudi_add_test(QMTest QMTEST)
 
-gaudi_install_python_modules()
+#gaudi_install_python_modules()
-- 
GitLab


From ab9e40266789533b516c66416a274d91209393c5 Mon Sep 17 00:00:00 2001
From: amathad <Abhijit Mathad amathad@cern.ch>
Date: Thu, 4 Mar 2021 11:04:04 +0100
Subject: [PATCH 5/6] RFix build warnings on platform x86_64-centos7-clang8-opt

---
 Phys/FunTuple/src/FunTuple.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Phys/FunTuple/src/FunTuple.cpp b/Phys/FunTuple/src/FunTuple.cpp
index c2de2c88c..69fd2a091 100755
--- a/Phys/FunTuple/src/FunTuple.cpp
+++ b/Phys/FunTuple/src/FunTuple.cpp
@@ -66,7 +66,10 @@ protected:
   StatusCode booktuple( const Tuples::Tuple& m_ntuple, const std::string& colname, const T2& val ) const;
   StatusCode checks();
   StatusCode setParticleTupleProps();
-  void       FindAndBookTuple( const unsigned int& i, const T& particles, const Tuples::Tuple& ntuple ) const {}
+  void       FindAndBookTuple( const unsigned int& i, const T& particles, const Tuples::Tuple& ntuple ) const {
+	  //variables used in explicit template specialisation, so suppress unused variable compiler warning here
+	  (void)i; (void)particles; (void)ntuple;
+  }
   //(Will be removed): The following overloaded function (makeMCTwoBodyDecay) are only used when m_makeCustomData is set
   // to true (see m_makeCustomData property description for more info)
   void makeMCTwoBodyDecay( LHCb::MCParticle& p, LHCb::MCParticle& p1, LHCb::MCParticle& p2, const int& parentid ) const;
-- 
GitLab


From cfa69df30d180304fa52d9348eda2d2f4f924da6 Mon Sep 17 00:00:00 2001
From: Gitlab CI <noreply@cern.ch>
Date: Thu, 4 Mar 2021 10:04:55 +0000
Subject: [PATCH 6/6] Fixed formatting

patch generated by https://gitlab.cern.ch/lhcb/Analysis/-/jobs/12472970
---
 Phys/FunTuple/src/FunTuple.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Phys/FunTuple/src/FunTuple.cpp b/Phys/FunTuple/src/FunTuple.cpp
index 69fd2a091..a820dafd6 100755
--- a/Phys/FunTuple/src/FunTuple.cpp
+++ b/Phys/FunTuple/src/FunTuple.cpp
@@ -67,8 +67,10 @@ protected:
   StatusCode checks();
   StatusCode setParticleTupleProps();
   void       FindAndBookTuple( const unsigned int& i, const T& particles, const Tuples::Tuple& ntuple ) const {
-	  //variables used in explicit template specialisation, so suppress unused variable compiler warning here
-	  (void)i; (void)particles; (void)ntuple;
+    // variables used in explicit template specialisation, so suppress unused variable compiler warning here
+    (void)i;
+    (void)particles;
+    (void)ntuple;
   }
   //(Will be removed): The following overloaded function (makeMCTwoBodyDecay) are only used when m_makeCustomData is set
   // to true (see m_makeCustomData property description for more info)
-- 
GitLab