From 74429c22bfba3754831c7005238eba8352b1ac30 Mon Sep 17 00:00:00 2001
From: Jonathan Burr <jon.burr@cern.ch>
Date: Mon, 30 Nov 2020 14:12:54 +0000
Subject: [PATCH] Add TTVA Isolation variables to derivations

---
 .../python/EGammaCommon.py                    | 11 +--
 .../python/MuonsCommon.py                     |  6 +-
 .../python/IsoUpdatedTrackCones.py            | 69 +++++++++++++++++++
 .../RecoTools/IsolationTool/CMakeLists.txt    |  1 +
 .../IsolationTool/TrackIsolationTool.h        | 13 ++++
 .../IsolationTool/Root/TrackIsolationTool.cxx | 37 +++++++---
 6 files changed, 118 insertions(+), 19 deletions(-)
 create mode 100644 Reconstruction/RecoAlgs/IsolationAlgs/python/IsoUpdatedTrackCones.py

diff --git a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkEGamma/python/EGammaCommon.py b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkEGamma/python/EGammaCommon.py
index cf21e8eff73..5f055fe057c 100644
--- a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkEGamma/python/EGammaCommon.py
+++ b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkEGamma/python/EGammaCommon.py
@@ -463,11 +463,6 @@ DerivationFrameworkJob += CfgMgr.DerivationFramework__CommonAugmentation("EGamma
 # ADD TOOLS
 #=======================================
 
-#
-# Disabling this tool due to it's missing in R22
-#
-# import IsolationAlgs.IsoUpdatedTrackCones as isoCones
-# if not hasattr(DerivationFrameworkJob,"IsolationBuilderTight1000"):
-#     DerivationFrameworkJob += isoCones.GetUpdatedIsoTrackCones()
-
-
+from IsolationAlgs.IsoUpdatedTrackCones import GetUpdatedIsoTrackCones
+if not hasattr(DerivationFrameworkJob,"IsolationBuilderTight1000"):
+    DerivationFrameworkJob += GetUpdatedIsoTrackCones()
\ No newline at end of file
diff --git a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkMuons/python/MuonsCommon.py b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkMuons/python/MuonsCommon.py
index 2d6c0169dba..96eb63474d6 100644
--- a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkMuons/python/MuonsCommon.py
+++ b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkMuons/python/MuonsCommon.py
@@ -68,6 +68,6 @@ DerivationFrameworkJob += CfgMgr.DerivationFramework__CommonAugmentation("DFComm
                                                                          AugmentationTools = DFCommonMuonToolWrapperTools
                                                                         )
 
-#import IsolationAlgs.IsoUpdatedTrackCones as isoCones
-#if not hasattr(DerivationFrameworkJob,"IsolationBuilderTight1000"):
-#  DerivationFrameworkJob += isoCones.GetUpdatedIsoTrackCones()
+from IsolationAlgs.IsoUpdatedTrackCones import GetUpdatedIsoTrackCones
+if not hasattr(DerivationFrameworkJob,"IsolationBuilderTight1000"):
+    DerivationFrameworkJob += GetUpdatedIsoTrackCones()
\ No newline at end of file
diff --git a/Reconstruction/RecoAlgs/IsolationAlgs/python/IsoUpdatedTrackCones.py b/Reconstruction/RecoAlgs/IsolationAlgs/python/IsoUpdatedTrackCones.py
new file mode 100644
index 00000000000..ace002a4830
--- /dev/null
+++ b/Reconstruction/RecoAlgs/IsolationAlgs/python/IsoUpdatedTrackCones.py
@@ -0,0 +1,69 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+from AthenaCommon.Logging import logging
+from AthenaCommon import CfgMgr
+
+log = logging.getLogger(__name__)
+
+
+def GetUpdatedIsoTrackCones(postfix="", object_types=("Electrons", "Photons", "Muons")):
+    """ Return a list of IsolationBuilder algorithms to calculate TTVA moments
+
+    ---------
+    Arguments
+    ---------
+    postfix: Optionally provide a postfix to add to the names of the isolation
+             builder algorithm instances
+    object_types: The object types to which to add the moments
+    """
+
+    import ROOT
+    # This is a doubly nested list
+    ptcone_list = [
+        [ROOT.xAOD.Iso.IsolationType.ptcone40, ROOT.xAOD.Iso.IsolationType.ptcone30, ROOT.xAOD.Iso.IsolationType.ptcone20]
+    ]
+
+    do_egamma = any(x in object_types for x in ("Electrons", "Photons"))
+
+    algs = []
+    for track_pt in (500, 1000):
+        for loose_cone in (True, False):
+            if loose_cone and not do_egamma:
+                # Loose cone isolation variables only for electrons and photons
+                continue
+            cone_str = "LooseCone" if loose_cone else ""
+            name = f"TightTTVA{cone_str}_pt{track_pt}"
+            # Build up extra IsolationBuilder kwargs
+            kwargs = {}
+            if "Electrons" in object_types:
+                kwargs["ElIsoTypes"] = ptcone_list
+                kwargs["ElCorTypes"] = [[]]
+                kwargs["ElCorTypesExtra"] = [[]]
+                kwargs["CustomConfigurationNameEl"] = name
+            if "Photons" in object_types:
+                kwargs["PhIsoTypes"] = ptcone_list
+                kwargs["PhCorTypes"] = [[]]
+                kwargs["PhCorTypesExtra"] = [[]]
+                kwargs["CustomConfigurationNamePh"] = name
+            if "Muons" in object_types:
+                kwargs["MuIsoTypes"] = ptcone_list
+                kwargs["MuCorTypes"] = [[]]
+                kwargs["MuCorTypesExtra"] = [[]]
+                kwargs["CustomConfigurationNameMu"] = name
+            algs.append(
+                CfgMgr.IsolationBuilder(
+                    f"IsolationBuilderTight{cone_str}{track_pt}{postfix}",
+                    TrackIsolationTool=CfgMgr.xAOD__TrackIsolationTool(
+                        f"TrackIsolationToolTight{track_pt}",
+                        TrackSelectionTool=CfgMgr.InDet__InDetTrackSelectionTool(
+                            minPt=track_pt, CutLevel="Loose"
+                        ),
+                        TTVATool=CfgMgr.CP__TrackVertexAssociationTool(
+                            WorkingPoint="Loose",
+                        ),
+                        CoreTrackEtaRange=0.01 if loose_cone else 0.0,
+                    ),
+                    **kwargs,
+                )
+            )
+    return algs
diff --git a/Reconstruction/RecoTools/IsolationTool/CMakeLists.txt b/Reconstruction/RecoTools/IsolationTool/CMakeLists.txt
index 60844d21711..e10b2d8ab69 100644
--- a/Reconstruction/RecoTools/IsolationTool/CMakeLists.txt
+++ b/Reconstruction/RecoTools/IsolationTool/CMakeLists.txt
@@ -26,6 +26,7 @@ atlas_add_library( IsolationToolLib
    xAODEventShape xAODMuon xAODPFlow xAODPrimitives xAODTracking
    AsgDataHandlesLib InDetTrackSelectionToolLib
    IsolationCorrectionsLib RecoToolInterfaces
+   TrackVertexAssociationToolLib
    ${extra_lib}
    PRIVATE_LINK_LIBRARIES ${Boost_LIBRARIES} CaloGeoHelpers FourMomUtils
    ${extra_lib_private}
diff --git a/Reconstruction/RecoTools/IsolationTool/IsolationTool/TrackIsolationTool.h b/Reconstruction/RecoTools/IsolationTool/IsolationTool/TrackIsolationTool.h
index f494ff17156..922627e806b 100644
--- a/Reconstruction/RecoTools/IsolationTool/IsolationTool/TrackIsolationTool.h
+++ b/Reconstruction/RecoTools/IsolationTool/IsolationTool/TrackIsolationTool.h
@@ -18,6 +18,7 @@
 #endif // XAOD_STANDALONE
 
 #include "InDetTrackSelectionTool/IInDetTrackSelectionTool.h"
+#include "TrackVertexAssociationTool/ITrackVertexAssociationTool.h"
 #include "xAODTracking/TrackParticle.h"
 #include "xAODTracking/TrackParticleContainer.h"
 #include "xAODTracking/VertexContainer.h"
@@ -123,6 +124,12 @@ namespace xAOD {
     Gaudi::Property<float> m_overlapCone{this, "OverlapCone", 0.1};
 
     float m_overlapCone2; /// overlap cone size squared
+
+    /// The maximum eta range to consider something a core track
+    Gaudi::Property<float> m_coreTrackEtaRange{this,
+     "CoreTrackEtaRange", 0.0, "The maximum eta range to consider something a core track"};
+    /// Whether the loose core track eta range is used
+    bool m_useLooseTrackCore{false};
 #ifndef XAOD_ANALYSIS
     /// tracks in cone tool
     ToolHandle<ITrackParticlesInConeTool> m_tracksInConeTool {this, 
@@ -132,6 +139,12 @@ namespace xAOD {
     ToolHandle<InDet::IInDetTrackSelectionTool> m_trkselTool {this,
 	"TrackSelectionTool", "InDet::InDetTrackSelectionTool/TrackSelectionTool"};
 
+    /// Select tracks associated to the vertex
+    ToolHandle<CP::ITrackVertexAssociationTool> m_ttvaTool{this,
+      "TTVATool", "", "Optional track to vertex association tool to filter tracks"};
+    /// Use the ttva tool. Set to true if one was provided.
+    bool m_useTTVATool{false};
+
     SG::ReadHandleKey<VertexContainer> m_vertexLocation {this,
 	"VertexLocation", "PrimaryVertices"};
 
diff --git a/Reconstruction/RecoTools/IsolationTool/Root/TrackIsolationTool.cxx b/Reconstruction/RecoTools/IsolationTool/Root/TrackIsolationTool.cxx
index a178903ba18..121c8ec86fb 100644
--- a/Reconstruction/RecoTools/IsolationTool/Root/TrackIsolationTool.cxx
+++ b/Reconstruction/RecoTools/IsolationTool/Root/TrackIsolationTool.cxx
@@ -48,6 +48,13 @@ namespace xAOD {
 	ATH_MSG_FATAL("Could not retrieve InDetTrackSelectionTool");
 	return StatusCode::FAILURE;
       }
+      if (!m_ttvaTool.empty()) {
+        ATH_MSG_DEBUG("Use TTVA tool " << m_ttvaTool);
+        m_useTTVATool = true;
+        ATH_CHECK( m_ttvaTool.retrieve() );
+      }
+      else
+        ATH_MSG_DEBUG("Will not use TTVA tool");
 
     /** square cone */
     m_overlapCone2 = m_overlapCone*m_overlapCone;
@@ -57,6 +64,9 @@ namespace xAOD {
     if (!m_vertexLocation.key().empty())
       ATH_CHECK(m_vertexLocation.initialize());
 
+    if (m_coreTrackEtaRange.value() > 0)
+      m_useLooseTrackCore = true;
+
     return StatusCode::SUCCESS;
   }
 
@@ -177,7 +187,8 @@ namespace xAOD {
 #endif
 
     for( const auto& tp : tps ) {
-      if( ! m_trkselTool->accept( *tp , input.vertex ) ){
+      if( (!m_trkselTool->accept( *tp , input.vertex)) ||
+        (m_useTTVATool && !m_ttvaTool->isCompatible(*tp, *input.vertex))){
 	ATH_MSG_DEBUG("reject track pt = " << tp->pt());
 	continue;
       } else
@@ -208,7 +219,8 @@ namespace xAOD {
 
     // loop over all track particles
     for( const auto& tp : *indetTrackParticles ) {
-      if( ! m_trkselTool->accept( *tp , input.vertex ) ){
+      if( (!m_trkselTool->accept(*tp, input.vertex)) ||
+        (m_useTTVATool && !m_ttvaTool->isCompatible(*tp, *input.vertex))){
 	ATH_MSG_DEBUG("[2] reject track pt = " << tp->pt());
 	continue;
       }
@@ -222,13 +234,22 @@ namespace xAOD {
   void TrackIsolationTool::add( TrackIsolationInput& input, const TrackParticle& tp2, TrackIsolation& result ) const
   {
     // check if track pointer matches the one of input or one of the exclusion set
+    // Jon Burr: I'm not completely convinced by the use of CoreTrackEtaRange.
+    // With this setup, if you're running in simple isolation mode any track
+    // within the same eta slice (even on the other side of the detector) will
+    // be included in the eta code. If you manually provide a track particle
+    // container then the tool automatically runs in this simple mode...
+    // This is fine in IsolationBuilder (the main client) but could produce
+    // unexpected results with other users
     if(input.corrections.trackbitset.test(static_cast<unsigned int>(Iso::coreTrackPtr))){
-	 if(input.particle == &tp2 || (input.exclusionSet && input.exclusionSet->count(&tp2))){
-	   ATH_MSG_DEBUG("track pointer " << &tp2 << ", track pt = " << tp2.pt() << ", input pt = " << input.particle->pt()) ;
-	   result.coreCorrections[Iso::coreTrackPtr] += tp2.pt();
-	   return;
-	 }
-       }
+      if(input.particle == &tp2 || 
+         (input.exclusionSet && input.exclusionSet->count(&tp2)) ||
+         (m_useLooseTrackCore && std::abs(input.particle->eta() - tp2.eta()) < m_coreTrackEtaRange)){
+        ATH_MSG_DEBUG("track pointer " << &tp2 << ", track pt = " << tp2.pt() << ", input pt = " << input.particle->pt()) ;
+        result.coreCorrections[Iso::coreTrackPtr] += tp2.pt();
+        return;
+      }
+    }
 
     // check eta
     float deta = input.particle->eta()-tp2.eta();
-- 
GitLab