From d6b201cf69335aaefcb27b2d0d056c2427f9bd3c Mon Sep 17 00:00:00 2001
From: Alex Pearce <alex.pearce@cern.ch>
Date: Fri, 10 Nov 2017 10:17:28 +0100
Subject: [PATCH] Add working neutral-matching logic, tidy up.

---
 Phys/Tesla/python/Tesla/Configuration.py | 173 ++++++++++++-----------
 1 file changed, 91 insertions(+), 82 deletions(-)

diff --git a/Phys/Tesla/python/Tesla/Configuration.py b/Phys/Tesla/python/Tesla/Configuration.py
index 3712f9b85..0e4933665 100644
--- a/Phys/Tesla/python/Tesla/Configuration.py
+++ b/Phys/Tesla/python/Tesla/Configuration.py
@@ -21,6 +21,7 @@ from Configurables import (
     DecodeRawEvent,
     DstConf,
     EventSelector,
+    Gaudi__DataLink as DataLink,
     GaudiSequencer,
     HltPackedDataDecoder,
     HltRoutingBitsFilter,
@@ -172,6 +173,7 @@ class Tesla(LHCbConfigurableUser):
     writerName = "DstWriter"
     teslaSeq = GaudiSequencer("TeslaSequence")
     base = "Turbo/"
+    neutral_pp2mc_loc = "Relations/Turbo/NeutralPP2MC"
 
 
     def _safeSet(self,conf,param):
@@ -211,12 +213,10 @@ class Tesla(LHCbConfigurableUser):
         SequencerTimerTool().OutputLevel = WARNING
 
     def _configureTrackTruth(self,assocpp,trackLoc) :
-        assoctr = TrackAssociator("TurboAssocTr"+trackLoc)
+        assoctr = TrackAssociator("TurboAssocTr"+trackLoc.replace('/', ''))
         assoctr.OutputLevel       = self.getProp('OutputLevel')
         assoctr.TracksInContainer = trackLoc
-        assoctr.TracksInContainer = trackLoc
         assocpp.TrackLocations   += [ trackLoc ]
-        #assocpp.OutputLevel = 1
         # Add it to a sequence
         return assoctr
 
@@ -229,18 +229,11 @@ class Tesla(LHCbConfigurableUser):
         clusterTabLoc = self.base + "Relations/CaloClusters"
 
         assoccluster = CaloClusterMCTruth("TurboClusterAssoc")
-        #assoccluster.OutputLevel = self.getProp('OutputLevel')
-        #assoccluster.OutputLevel = 1
+        assoccluster.OutputLevel = self.getProp('OutputLevel')
         assoccluster.Input     = digits
         assoccluster.Output    = clusterTabLoc
-        #assoccluster.Clusters += clusters
-        assoccluster.Clusters += [ "/Event/Turbo/PID/Calo/EcalClusters", "/Event/Turbo/PID/Calo/EcalSplitClusters", "/Event/Turbo/PID/Calo/CaloClusters" ]
+        assoccluster.Clusters += clusters
 
-        # When filtering MC, the relations table cloners will copy the tables
-        # to /Event/Turbo for us. Otherwise we create them there directly
-        #protoTabLoc = "Relations/Turbo/NeutralPP2MC"
-        if not self.getProp("FilterMC"):
-            protoTabLoc = os.path.join(self.base, protoTabLoc)
         assocneutral = NeutralPP2MC("TurboNeutralPP2MC")
         assocneutral.InputData += protos
         assocneutral.OutputLevel = self.getProp('OutputLevel')
@@ -248,7 +241,7 @@ class Tesla(LHCbConfigurableUser):
         assocneutral.MCCaloTable = clusterTabLoc
 
         retSeq.Members += [assoccluster,assocneutral]
-        return protoTabLoc, retSeq
+        return retSeq
 
     def _unpackMC(self):
         DataOnDemandSvc().NodeMap['/Event/MC']   = 'DataObject'
@@ -500,7 +493,8 @@ class Tesla(LHCbConfigurableUser):
 
             # Configure
             self._configureDigitsTruth()
-            protoneutral, retSeq = self._configureClustersAndProtosTruth(outputDigiLoc,neutralClustersTot,neutralProtosTot)
+            protoneutral = os.path.join(tesROOT, self.neutral_pp2mc_loc)
+            retSeq = self._configureClustersAndProtosTruth(outputDigiLoc,neutralClustersTot,neutralProtosTot,protoneutral)
             NeutralProtoSeq.Members+=[retSeq]
 
             # Add standard Turbo locations if not packing
@@ -598,81 +592,96 @@ class Tesla(LHCbConfigurableUser):
         TeslaReportAlgoSeq.Members += [TeslaOutputStreamsSequence]
 
     def _configureTruthMatching(self, name, packing):
+        # Locations of the objects we want to truth-match
+        track_locs = [packing.outputs['Hlt2LongTracks'],
+                      packing.outputs['Hlt2DownstreamTracks']]
+        proto_locs = [packing.outputs['Hlt2LongProtos'],
+                      packing.outputs['Hlt2DownstreamProtos']]
+        cluster_locs = [packing.outputs['Hlt2CaloClusters']]
+        neutral_locs = [packing.outputs['Hlt2NeutralProtos']]
+
+        # When filtering MC, the relations table cloners will copy the
+        # tables to /Event/Turbo for us. Otherwise we create them there
+        # directly
+        tesROOT = "/Event"
+        if not self.getProp("FilterMC"):
+            tesROOT = os.path.join(tesROOT, self.base)
 
-        # CHARGED STUFF #####################################################################
-        # Get the location of all tracks and all protoparticles we want to truthmatch
-        track_locs = [v for v in packing.outputs.values() if 'track' in v.lower()]
-        proto_locs = [v for v in packing.outputs.values() if 'proto' in v.lower()]
-        
-        ChargedProtoSeq = GaudiSequencer("ChargedTruthSequencer")
         assocpp = ChargedPP2MC("TurboProtoAssocPP")
         assocpp.OutputLevel = self.getProp('OutputLevel')
-        #assocpp.OutputLevel = 1
-        assocpp.VetoEmpty=True
-        #assocpp.RootInTES = '/Event/Turbo'
+        assocpp.VetoEmpty = True
+        assocpp.RootInTES = tesROOT
         # Make all track -> MCParticle linker tables
-        for loc in track_locs:
-            truthSeq = self._configureTrackTruth(assocpp,loc)
-            ChargedProtoSeq.Members.append( truthSeq )
+        trackTruthSeq = GaudiSequencer('TurboTrackTruthSequencer')
+        trackTruthSeq.Members = [self._configureTrackTruth(assocpp, loc)
+                                 for loc in track_locs]
         # Make the protoparticle -> track linker tables
         assocpp.InputData += proto_locs
-        ChargedProtoSeq.Members.append( assocpp )
-        #relationsLocations = [os.path.join('Relations', path)
-        #                      for path in assocpp.InputData]
-        #relationsLocations.append(protoneutral)
-        print "++++++++++++++++"
-        print proto_locs
-        relationsLocations = [ loc.replace('/Event', '/Event/Relations') for loc in proto_locs ]
-        print relationsLocations
-        print "++++++++++++++++"
-        filterMCSeq = self._filterMCParticlesSequence(relationsLocations)
-        ChargedProtoSeq.Members += [filterMCSeq]
-
-        
-        # NEUTRAL STUFF ################################################################
-        NeutralProtoSeq = GaudiSequencer("NeutralTruthSequencer")
 
-        ## Add the digits associator
-        assocdigits = CaloDigit2MCLinks2Table("TurboDigitAssoc")
-        #assocdigits.OutputLevel = self.getProp('OutputLevel')
-        #assocdigits.OutputLevel = 1
-        outputDigiLoc = self.base+"Relations/CaloDigits"
-        assocdigits.Output = outputDigiLoc
-        assocdigits.Inputs = [ "Turbo/Raw/Ecal/Digits", "Turbo/Raw/Hcal/Digits"]
-        
-        ## Finally configure the neutral PP2MC associator
-        NeutralProtoSeq.Members+=[assocdigits]
-        
-        # Gather all protos and clusters
-        neutralClustersTot=[]
-        neutralProtosTot=[]
-
-        print " ------> Neutral stuff <-------------"
-        print self.base
-        print " ------> Neutral stuff <-------------"
-        
-        # Add standard Turbo
-        #neutralClustersTot+=[self.base+"Hlt2/Rec/Neutral/CaloClusters"]
-        neutralClustersTot+=[self.base+"CaloClusters"]
-        neutralProtosTot+=[self.base+"Protos"]
-
-        print " ------> Neutral stuff <-------------"
-        print neutralClustersTot
-        print " ------> Neutral stuff <-------------"
-        
-        
-        # Configure
-        self._configureDigitsTruth()
-        protoneutral, retSeq = self._configureClustersAndProtosTruth(outputDigiLoc,neutralClustersTot,neutralProtosTot, "Turbo/Relations/NeutralPP2MC")
-        NeutralProtoSeq.Members+=[retSeq]
+        assocDigits = CaloDigit2MCLinks2Table("TurboDigitAssoc")
+        # The digits relations table maker will try to find the linker table
+        # for digits at `Raw/{det}/Digits` at `Link/Raw/{det}/Digits`. Our
+        # digits are at `Turbo/Raw/{det}/Digits`, but we can't remake a linker
+        # table at `Link/Turbo/Raw/{det}/Digits because the information needed
+        # to make one is lost after Boole.
+        # Instead, exploit the fact that the digits under `Turbo/` are a subset
+        # of the originals, and just symlink the original linker table to the
+        # location expected for the Turbo digits
+        assocDigits.RespectInputDigitLocation = False
+        digit_locs = [l.replace('/Event/', '')
+                      for l in assocDigits.getDefaultProperty('Inputs')]
+        turbo_digit_locs = [os.path.join(self.base, l) for l in digit_locs]
+        linkingSeq = GaudiSequencer('TurboLinkTableLinkers')
+        for digit_loc in digit_locs:
+            link_loc = os.path.join('Link', digit_loc)
+            turbo_link_loc = os.path.join('Link', self.base, digit_loc)
+            name = '{0}Linker'.format(link_loc.replace('/', ''))
+            dl = DataLink(name, What=link_loc, Target=turbo_link_loc)
+            linkingSeq.Members.append(dl)
+            # DataLinks don't create the ancestor TES structure, so do it
+            # manually
+            path = ''
+            for node in turbo_link_loc.split(os.path.sep)[:-1]:
+                path = os.path.join(path, node)
+                DataOnDemandSvc().NodeMap[path] = 'DataObject'
+
+        # Add the CaloDigit <-> MCParticle associator
+        assocDigits.OutputLevel = self.getProp('OutputLevel')
+        # Can use a magic string here as this table is only temporary
+        assocDigits.Output = "Relations/CaloDigits"
+        assocDigits.Inputs = turbo_digit_locs
+
+        # Add the CaloCluster <-> MCParticle and neutral ProtoParticle <->
+        # MCParticle associators
+        neutral_rels_loc = os.path.join(tesROOT, self.neutral_pp2mc_loc)
+        assocClustersNeutrals = self._configureClustersAndProtosTruth(
+            assocDigits.Output,
+            cluster_locs,
+            neutral_locs,
+            neutral_rels_loc
+        )
 
+        chargedProtoSeq = GaudiSequencer("ChargedTruthSequencer")
+        chargedProtoSeq.Members = [trackTruthSeq, assocpp]
+        neutralProtoSeq = GaudiSequencer("NeutralTruthSequencer")
+        neutralProtoSeq.Members = [linkingSeq, assocDigits, assocClustersNeutrals]
+        truthSeq = GaudiSequencer("TurboTruthSequencer")
+        truthSeq.Members = [chargedProtoSeq, neutralProtoSeq]
+
+        # Sequence to copy the relations tables, and the subset of MC
+        # particles used by those tables, into /Event/Turbo
+        if self.getProp("FilterMC"):
+            relationsLocations = [
+                os.path.join('Relations', path.replace('/Event/', ''))
+                for path in assocpp.InputData
+            ]
+            relationsLocations.append(neutral_rels_loc)
+            filterMCSeq = self._filterMCParticlesSequence(relationsLocations)
+            truthSeq.Members += [filterMCSeq]
 
+        return truthSeq
 
 
-        
-        return NeutralProtoSeq
-        
-        
     def _configureOutputStream(self, stream, lines_dict):
         '''
         The lines dictionary should have the following form:
@@ -860,7 +869,8 @@ class Tesla(LHCbConfigurableUser):
                 #decoder.OutputLevel = 1
                 decoders_seq.Members.append(decoder)
 
-            DecodeRawEvent().DataOnDemand = False
+            # Need the DoD for decoding the Ecal digits
+            DecodeRawEvent().DataOnDemand = simulation
             self._safeSet(DstConf(), ['SplitRawEventOutput'])
 
         # This decoder is setup by TurboConf
@@ -998,11 +1008,10 @@ class Tesla(LHCbConfigurableUser):
                 ApplicationMgr().ExtSvc.append(dod)
             # Check that the DoD is running
             #dod.OutputLevel = 1 
-            
+
             self._unpackMC()
             stream_seq.Members.append(self._configureTruthMatching(name, packing))
-           
-            
+
         # No need to clone if the output prefix is empty (everything stays
         # under /Event/Turbo)
         if output_prefix:
-- 
GitLab