From 20df393f9f9c130b7760109488a6321b4f4fe612 Mon Sep 17 00:00:00 2001
From: Davide Fazzini <davide.fazzini@cern.ch>
Date: Mon, 21 Mar 2022 09:15:35 +0100
Subject: [PATCH] Adding qmtest for ParticleTaggerAlg and related functors

---
 .../option_davinci_tupling_array_taggers.py   |  88 +++++++++++++++
 .../test_davinci_tupling_array_taggers.qmt    |  43 ++++++++
 .../test_davinci_tupling_array_taggers.ref    | 100 ++++++++++++++++++
 .../python/DaVinci/standard_particles.py      |  57 ++++++++++
 4 files changed, 288 insertions(+)
 create mode 100644 DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py
 create mode 100644 DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_array_taggers.qmt
 create mode 100644 DaVinciExamples/tests/refs/test_davinci_tupling_array_taggers.ref

diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py
new file mode 100644
index 000000000..2f05c38f4
--- /dev/null
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py
@@ -0,0 +1,88 @@
+###############################################################################
+# (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.                                       #
+###############################################################################
+"""
+ Option file for testing the ParticleTaggerAlg algorithm and the related ThOr functors MAP_ARRAY and MAP_RANGE.
+ The job runs over a spruced sample and retrieves a set of B0 -> Ds K+ candidates. For each candidate the ParticleTaggerAlg
+ looks at the TES location defined via the 'make_long_pions_from_spruce' function and creates a 'one-to-many' relation map
+ relating all the available tracks to the B candidate of the events. 
+ Then the MAP_ARRAY functor takes in input this relation map and for each entry stores the output of an external functor (i.e F.P, F.PT)
+ in a vector via the MAP_RANGE functor.
+
+ This example is meant to be run with
+    $ ./run davinci run-mc --inputfiledb Spruce_all_lines_dst Phys/DaVinci/options/DaVinciDB-Example.yaml --user_algorithms DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers:main
+"""
+
+import Functors as F
+from PyConf.application import make_data_with_FetchDataFromFile
+from PyConf.Algorithms import ParticleTaggerAlg, ParticleContainerMerger
+from FunTuple import FunctorCollection, FunTuple_Particles as Funtuple
+from DaVinci.algorithms import add_filter
+from DaVinci.standard_particles import make_long_pions_from_spruce
+
+bd2dsk_line = "SpruceB2OC_BdToDsmK_DsmToHHH_FEST_Line"
+bd2dsk_data = make_data_with_FetchDataFromFile(
+    f"/Event/Spruce/{bd2dsk_line}/Particles")
+pions = make_long_pions_from_spruce()
+
+tagging_container = ParticleContainerMerger(
+    InputContainers=[pions]).OutputContainer
+
+tagAlg = ParticleTaggerAlg(
+    Input=bd2dsk_data, TaggingContainer=tagging_container, OutputLevel=3)
+tagAlg_rels = tagAlg.OutputRelations
+
+#make collection of functors
+variables_B = FunctorCollection({
+    'THOR_MASS':
+    F.MASS,
+    "TagTr_P":
+    F.MAP_INPUT_ARRAY(Functor=F.MAP_RANGE(Functor=F.P), Relations=tagAlg_rels),
+    "TagTr_PT":
+    F.MAP_INPUT_ARRAY(
+        Functor=F.MAP_RANGE(Functor=F.PT), Relations=tagAlg_rels),
+})
+
+#make collection of functors for Muplus
+variables_all = FunctorCollection({
+    'THOR_P': F.P,
+    'THOR_PT': F.PT,
+})
+
+fields = {
+    'B0': "[B0 -> D_s- K+]CC",
+    'Ds': "[B0 -> ^D_s- K+]CC",
+    'Kp': "[B0 -> D_s- ^K+]CC",
+}
+
+variables = {
+    'ALL': variables_all,  #adds variables to all fields
+    'B0': variables_B,
+}
+
+tuple_B0DsK = Funtuple(
+    name="B0DsK_Tuple",
+    tuple_name="DecayTree",
+    fields=fields,
+    variables=variables,
+    inputs=bd2dsk_data)
+
+filter_B0DsK = add_filter("HDRFilter_B0DsK",
+                          f"HLT_PASS('{bd2dsk_line}Decision')")
+
+from DaVinci import options
+options.annsvc_config = 'root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/spruce_all_lines_realtime.tck.json'
+options.histo_file = 'DV-example-tagger-his.root'
+options.ntuple_file = 'DV-example-tagger-ntp.root'
+
+
+def main():
+    algs = [filter_B0DsK, tuple_B0DsK]
+    return algs, []
diff --git a/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_array_taggers.qmt b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_array_taggers.qmt
new file mode 100644
index 000000000..e99775453
--- /dev/null
+++ b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_array_taggers.qmt
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?>
+<!--
+###############################################################################
+# (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.                                       #
+###############################################################################
+-->
+<!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: dfazzini
+# Purpose: Test for the ParticleTaggerAlg algorithm and the related MAP_ARRAY and MAP_RANGE functors
+# Prerequisites: None
+# inputfiledb Spruce_all_lines_dst $DAVINCIROOT/options/DaVinciDB-Example.yaml
+# user_algorithms ../../python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers:main
+#######################################################
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+  <argument name="program"><text>davinci</text></argument>
+  <argument name="args"><set>
+  <text>run-mc</text>
+  <text>--inputfiledb</text>
+  <text>Spruce_all_lines_dst</text>
+  <text>$DAVINCIROOT/options/DaVinciDB-Example.yaml</text>
+  <text>--user_algorithms</text>
+  <text>../../python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers:main</text>
+  </set></argument>
+  <argument name="reference"><text>../refs/test_davinci_tupling_array_taggers.ref</text></argument>
+  <argument name="error_reference"><text>../refs/empty.ref</text></argument>
+  <argument name="validator"><text>
+from DaVinciTests.QMTest.DaVinciExclusions import preprocessor
+validateWithReference(preproc = preprocessor)
+countErrorLines({"FATAL":0, "ERROR":0})
+</text></argument>
+</extension>
diff --git a/DaVinciExamples/tests/refs/test_davinci_tupling_array_taggers.ref b/DaVinciExamples/tests/refs/test_davinci_tupling_array_taggers.ref
new file mode 100644
index 000000000..60a02b2a1
--- /dev/null
+++ b/DaVinciExamples/tests/refs/test_davinci_tupling_array_taggers.ref
@@ -0,0 +1,100 @@
+INFO No MainOptions specified. DaVinci will import no options file!
+INFO User algorithm option_davinci_tupling_array_taggers.main imported successfully!
+ApplicationMgr    SUCCESS
+====================================================================================================================================
+====================================================================================================================================
+ApplicationMgr       INFO Application Manager Configured successfully
+DetectorPersistencySvc                 INFO Added successfully Conversion service:XmlCnvSvc
+DetectorDataSvc                     SUCCESS Detector description database: git:/lhcb.xml
+NTupleSvc                              INFO Added stream file:DV-example-tagger-ntp.root as FILE1
+RootHistSvc                            INFO Writing ROOT histograms to: DV-example-tagger-his.root
+HistogramPersistencySvc                INFO Added successfully Conversion service:RootHistSvc
+FSROutputStreamDstWriter               INFO Data source: EventDataSvc output: SVC='Gaudi::RootCnvSvc'
+EventClockSvc.FakeEventTime            INFO Event times generated from 0 with steps of 0
+Unpacker_PackedChargedProtos.Cha...    INFO Using retuned RICH el and mu DLL values in combined DLLs
+FunctionalParticleMaker.LoKi::Hy...    INFO CUT: ' ( (TrTYPE==3) &TrALL) '
+ApplicationMgr                         INFO Application Manager Initialized successfully
+DeFTDetector                           INFO Current FT geometry version =   63
+ApplicationMgr                         INFO Application Manager Started successfully
+EventPersistencySvc                    INFO Added successfully Conversion service:RootCnvSvc
+EventSelector                       SUCCESS Reading Event record 1. Record number within stream 1: 1
+EventPersistencySvc                    INFO Added successfully Conversion service:LHCb::RawDataCnvSvc
+RFileCnv                               INFO opening Root file "DV-example-tagger-ntp.root" for writing
+RCWNTupleCnv                           INFO Booked TTree with ID: DecayTree "DecayTree" in directory DV-example-tagger-ntp.root:/B0DsK_Tuple
+ApplicationMgr                         INFO Application Manager Stopped successfully
+FSROutputStreamDstWriter               INFO Set up File Summary Record
+FSROutputStreamDstWriter               INFO Events output: 1
+B0DsK_Tuple                         SUCCESS Booked 1 N-Tuples and 0 Event Tag Collections
+B0DsK_Tuple                         SUCCESS List of booked N-Tuples in directory "FILE1/B0DsK_Tuple"
+B0DsK_Tuple                         SUCCESS  ID=DecayTree     Title="DecayTree"                               #items=10 {B0_THOR_MASS,indx,B0_TagTr_P[1]/V,B0_TagTr_PT[1]/V,B0_THOR_P,B0_THOR_PT,Ds_THOR_P}
+LAZY_AND: DaVinci                                         #=110     Sum=79          Eff=|( 71.81818 +- 4.28949 )%|
+ NONLAZY_OR: FileSummaryRecords                           #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+  LAZY_AND: GenFSR                                        #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   RecordStream/FSROutputStreamDstWriter                  #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+ NONLAZY_OR: UserAnalysis                                 #=110     Sum=79          Eff=|( 71.81818 +- 4.28949 )%|
+  LAZY_AND: UserAlgorithms                                #=110     Sum=79          Eff=|( 71.81818 +- 4.28949 )%|
+   LHCb__UnpackRawEvent/LHCb__UnpackRawEvent              #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   HltPackedDataDecoder/HltPackedDataDecoder              #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackMCParticle/UnpackMCParticle                      #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackMCVertex/UnpackMCVertex                          #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackTrack/UnpackTracks                               #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackRecVertex/UnpackPVs                              #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackProtoParticle/UnpackChargedProtos                #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackProtoParticle/UnpackNeutralProtos                #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackCaloHypo/UnpackCaloElectrons                     #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackCaloHypo/UnpackCaloPhotons                       #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackCaloHypo/UnpackCaloMergedPi0s                    #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackCaloHypo/UnpackCaloSplitPhotons                  #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   MuonPIDUnpacker/UnpackMuonPIDs                         #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   RichPIDUnpacker/UnpackRichPIDs                         #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   UnpackParticlesAndVertices/UnpackParticlesAndVertices  #=110     Sum=110         Eff=|( 100.0000 +- 0.00000 )%|
+   LoKi__HDRFilter/HDRFilter_B0DsK                        #=110     Sum=79          Eff=|( 71.81818 +- 4.28949 )%|
+   FunTupleBase_Particles/B0DsK_Tuple                     #=79      Sum=79          Eff=|( 100.0000 +- 0.00000 )%|
+ToolSvc                                INFO Removing all tools created by ToolSvc
+RFileCnv                               INFO dumping contents of /NTUPLES/FILE1
+TFile: name=DV-example-tagger-ntp.root, title=Gaudi Trees, option=CREATE
+NTupleSvc                              INFO NTuples saved successfully
+ApplicationMgr                         INFO Application Manager Finalized successfully
+ApplicationMgr                         INFO Application Manager Terminated successfully
+B0DsK_Tuple                            INFO Number of counters : 7
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# events with multiple candidates for field B0"|        64 |
+ | "# events with multiple candidates for field Ds"|        64 |
+ | "# events with multiple candidates for field Kp"|        64 |
+ | "# non-empty events for field B0"               |        79 |
+ | "# non-empty events for field Ds"               |        79 |
+ | "# non-empty events for field Kp"               |        79 |
+ | "# processed events"                            |        79 |
+FunctionalParticleMaker                INFO Number of counters : 4
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ |*"# passed ProtoParticle filter"                 |       618 |        618 |( 100.0000 +-  0.000000)% |
+ |*"# passed Track filter"                         |       631 |        618 |( 97.93978 +- 0.5654860)% |
+ | "Nb created anti-particles"                     |        79 |        307 |     3.8861 |     2.0188 |      2.0000 |      10.000 |
+ | "Nb created particles"                          |        79 |        311 |     3.9367 |     2.0459 |      2.0000 |      10.000 |
+HDRFilter_B0DsK                        INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ |*"#passed"                                       |       110 |         79 |( 71.81818 +- 4.289487)% |
+ParticleContainerMerger                INFO Number of counters : 2
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# input particles"                             |        79 |        618 |     7.8228 |
+ | "# output particles"                            |       618 |          0 |      0.0000 |
+ParticleTaggerAlg                      INFO Number of counters : 3
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "Events"                                        |        79 |
+ | "Input Particles"                               |        79 |        460 |     5.8228 |     13.704 |      1.0000 |      122.00 |
+ | "Output Particles"                              |        79 |        618 |     7.8228 |     3.9322 |      4.0000 |      20.000 |
+ToolSvc.HltFactory                     INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# loaded from PYTHON"                          |         1 |
+ToolSvc.PPFactoryHybridFactory         INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# loaded from PYTHON"                          |         1 |
+ToolSvc.TrackFunctorFactory            INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# loaded from PYTHON"                          |         1 |
+Unpacker_PackedMuonPIDs                INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# UnPackedData"                                |        79 |          0 |      0.0000 |      0.0000 |       0.0000 |       0.0000 |
+Unpacker_PackedRichPIDs                INFO Number of counters : 1
+ |    Counter                                      |     #     |    sum     | mean/eff^* | rms/err^*  |     min     |     max     |
+ | "# UnPackedData"                                |        79 |        631 |     7.9873 |     3.9984 |      4.0000 |      20.000 |
diff --git a/Phys/DaVinci/python/DaVinci/standard_particles.py b/Phys/DaVinci/python/DaVinci/standard_particles.py
index 69e5b1bb1..5af8ab513 100644
--- a/Phys/DaVinci/python/DaVinci/standard_particles.py
+++ b/Phys/DaVinci/python/DaVinci/standard_particles.py
@@ -635,3 +635,60 @@ def make_mass_constrained_jpsi2mumu(name='MassConstrJpsi2MuMuMaker',
     )
 
     return ParticleFilter(dimuons, name=name, Code=code)
+
+
+# Temporary function implemented for testing the MAP_ARRAY functor and ParticleTaggerAlg algorithm
+# in DaVinciExamples.tupling.test_davinci_tupling_array_taggers.qmt.
+# Aim: create long pions particles from Spruce TES location since the standard '/Event/pRec',
+# used in all the other make functions, is not available.
+# TO BE REMOVED AS SOON AS THIS PYTHON MODULE IS MOVED INTO ANOTHER SHARED REPO OR
+# IT'S REDESIGNED SPECIFICALLY FOR DAVINCI.
+@configurable
+def make_long_pions_from_spruce(stream="/Event/Spruce/HLT2"):
+    from Configurables import MuonPIDUnpacker, RichPIDUnpacker, UnpackProtoParticle
+    from PyConf.application import make_data_with_FetchDataFromFile
+    from PyConf.components import Algorithm, force_location
+    from PyConf.Tools import (ChargedProtoParticleAddRichInfo,
+                              ChargedProtoParticleAddMuonInfo,
+                              ChargedProtoParticleAddCombineDLLs)
+    from DaVinci.locations import LocationsUnpackedReco
+
+    # Define a new reco_unpackers taking Spruce locations as input
+    def reco_unpacker(key, configurable, input_location, **kwargs):
+        """
+        Return a `PyConf.Algorithm` instance that reading from a given input location unpacks a specific reconstructed object, identified by a 'key', to the
+        forced output location `LocationsUnpackedReco[key]`.
+        """
+        alg = Algorithm(
+            configurable,
+            name=f"Unpacker_{key}",
+            InputName=make_data_with_FetchDataFromFile(input_location),
+            outputs={
+                "OutputName": force_location(LocationsUnpackedReco[key].value)
+            },
+            **kwargs)
+        return alg
+
+    richPIDs = reco_unpacker("PackedRichPIDs", RichPIDUnpacker,
+                             f"{stream}/pRec/Rich/PIDs")
+    muonPIDs = reco_unpacker("PackedMuonPIDs", MuonPIDUnpacker,
+                             f"{stream}/pRec/Muon/MuonPID")
+
+    charged_protos = reco_unpacker(
+        "PackedChargedProtos",
+        UnpackProtoParticle,
+        f"{stream}/pRec/ProtoP/Charged",
+        AddInfo=[
+            ChargedProtoParticleAddRichInfo(
+                InputRichPIDLocation=richPIDs.OutputName),
+            ChargedProtoParticleAddMuonInfo(
+                InputMuonPIDLocation=muonPIDs.OutputName),
+            ChargedProtoParticleAddCombineDLLs()
+        ])
+
+    particles = FunctionalParticleMaker(
+        InputProtoParticles=charged_protos,
+        ParticleID="pion",
+        TrackSelector=get_long_track_selector(),
+        ProtoParticleFilter=standard_protoparticle_filter()).Particles
+    return particles
-- 
GitLab