From 538fffd658fa151aa7bd1419c0c5dcc266b1f4a9 Mon Sep 17 00:00:00 2001 From: Heather Russell <heather.russell@cern.ch> Date: Thu, 30 May 2024 11:37:53 +0200 Subject: [PATCH] Update AsgLeptonTrackSelectionAlg to write out d0 and z0sinTheta variables Update AsgLeptonTrackSelectionAlg to write out d0 and z0sinTheta variables --- .../data/for_compare.yaml | 3 + .../python/ConfigText_unitTest.py | 2 + .../python/FullCPAlgorithmsTest.py | 4 +- .../AsgLeptonTrackSelectionAlg.h | 10 ++ .../Root/AsgLeptonTrackSelectionAlg.cxx | 107 ++++++++++-------- .../python/ElectronAnalysisConfig.py | 17 ++- .../python/MuonAnalysisConfig.py | 13 ++- 7 files changed, 103 insertions(+), 53 deletions(-) diff --git a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/data/for_compare.yaml b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/data/for_compare.yaml index 44d4ac7e83bb..94ea71b134e6 100644 --- a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/data/for_compare.yaml +++ b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/data/for_compare.yaml @@ -30,6 +30,8 @@ Electrons: likelihoodWP: 'LooseBLayerLH' isolationWP: 'Loose_VarRad' recomputeLikelihood: False + writeTrackD0Z0: True + PtEtaSelection: minPt: 10000.0 @@ -54,6 +56,7 @@ Muons: quality: 'Medium' isolation: 'Loose_VarRad' onlyRecoEffSF: True + writeTrackD0Z0: True PtEtaSelection: {} TauJets: diff --git a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigText_unitTest.py b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigText_unitTest.py index 5c18b175f475..c47bfde54da1 100755 --- a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigText_unitTest.py +++ b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/ConfigText_unitTest.py @@ -99,6 +99,7 @@ def compareTextBuilder(yamlPath='', *, checkOrder=False) : config.setOptions (likelihoodWP='LooseBLayerLH') config.setOptions (isolationWP='Loose_VarRad') config.setOptions (recomputeLikelihood=False) + config.setOptions (writeTrackD0Z0=True) # Electrons.PtEtaSelection config.addBlock ('Electrons.PtEtaSelection') config.setOptions (containerName='AnaElectrons') @@ -133,6 +134,7 @@ def compareTextBuilder(yamlPath='', *, checkOrder=False) : config.setOptions (quality='Medium') config.setOptions (isolation='Loose_VarRad') config.setOptions (onlyRecoEffSF=True) + config.setOptions (writeTrackD0Z0=True) # Muons.PtEtaSelection config.addBlock ('Muons.PtEtaSelection') config.setOptions (containerName='AnaMuons') diff --git a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/FullCPAlgorithmsTest.py b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/FullCPAlgorithmsTest.py index 0c4dc6fd91c0..3acf116d564a 100644 --- a/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/FullCPAlgorithmsTest.py +++ b/PhysicsAnalysis/Algorithms/AnalysisAlgorithmsConfig/python/FullCPAlgorithmsTest.py @@ -791,6 +791,7 @@ def makeSequenceBlocks (dataType, algSeq, forCompare, isPhyslite, configSeq.setOptionValue ('.likelihoodWP', 'LooseDNN') configSeq.setOptionValue ('.isolationWP', 'Loose_VarRad') configSeq.setOptionValue ('.recomputeLikelihood', recomputeLikelihood) + configSeq.setOptionValue ('.writeTrackD0Z0', True) configSeq += config.makeConfig ('Electrons.PtEtaSelection', containerName='AnaElectrons') @@ -835,6 +836,8 @@ def makeSequenceBlocks (dataType, algSeq, forCompare, isPhyslite, configSeq.setOptionValue ('.isolation', 'Loose_VarRad') if forCompare : configSeq.setOptionValue ('.onlyRecoEffSF', True) + configSeq.setOptionValue ('.writeTrackD0Z0', True) + # TODO: MCP should restore this when the recommendations for Tight WP exist in R23 # configSeq += config.makeConfig ('Muons.Selection', 'AnaMuons.tight') # configSeq.setOptionValue ('.quality', 'Tight') @@ -846,7 +849,6 @@ def makeSequenceBlocks (dataType, algSeq, forCompare, isPhyslite, configSeq.setOptionValue ('.minPt', muonMinPt) configSeq.setOptionValue ('.maxEta', muonMaxEta) - # Include, and then set up the tau analysis algorithm sequence: configSeq += config.makeConfig ('TauJets', containerName='AnaTauJets') diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h index e030d4585b03..bb2f8b8e5bfd 100644 --- a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h +++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/AsgAnalysisAlgorithms/AsgLeptonTrackSelectionAlg.h @@ -57,6 +57,9 @@ namespace CP Gaudi::Property<int> m_nMaxPixelHits {this, "nMaxPixelHits", -1, "maximum number of required Pixel hits (or -1 for no cut)"}; Gaudi::Property<int> m_nMinSCTHits {this, "nMinSCTHits", -1, "minimum number of required SCT hits (or -1 for no cut)"}; Gaudi::Property<int> m_nMaxSCTHits {this, "nMaxSCTHits", -1, "maximum number of required SCT hits (or -1 for no cut)"}; + Gaudi::Property<bool> m_decorateTTVAVars{this, "decorateTTVAVars", false, "save the calculated d0sig and z0sinTheta variables"}; + Gaudi::Property<std::string> m_d0sigDecoration {this, "d0sigDecoration", "", "the decoration name for d0 significance"}; + Gaudi::Property<std::string> m_z0sinthetaDecoration {this, "z0sinthetaDecoration", "", "the decoration name for z0sintheta"}; /// \} @@ -90,6 +93,13 @@ namespace CP private: ServiceHandle<ISelectionNameSvc> m_nameSvc {"SelectionNameSvc", "AsgLeptonTrackSelectionAlg"}; + /// \brief the name of the variable being decorated for d0significance + private: + std::unique_ptr<const SG::AuxElement::Decorator<float> > m_d0sigDecorator {}; + + /// \brief the name of the variable being decorated for z0sintheta + private: + std::unique_ptr<const SG::AuxElement::Decorator<float> > m_z0sinthetaDecorator {}; /// \brief the \ref asg::AcceptInfo we are using private: diff --git a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx index 60665a2a700e..24572e712f43 100644 --- a/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx +++ b/PhysicsAnalysis/Algorithms/AsgAnalysisAlgorithms/Root/AsgLeptonTrackSelectionAlg.cxx @@ -48,6 +48,28 @@ namespace CP if (m_nMinSCTHits != -1 || m_nMaxSCTHits != -1) m_accept.addCut ("numSCTHits", "Minimum and/or maxiumum SCT hits"); + if(m_decorateTTVAVars){ + if (m_d0sigDecoration.empty()){ + ANA_MSG_ERROR ("No d0significance decoration name set"); + return StatusCode::FAILURE; + } else { + m_d0sigDecorator = std::make_unique<SG::AuxElement::Decorator<float> > (m_d0sigDecoration); + } + if (m_z0sinthetaDecoration.empty()){ + ANA_MSG_ERROR ("No z0sintheta decoration name set"); + return StatusCode::FAILURE; + } else { + m_z0sinthetaDecorator = std::make_unique<SG::AuxElement::Decorator<float> > (m_z0sinthetaDecoration); + } + } else{ + if (!m_d0sigDecoration.empty()){ + ANA_MSG_WARNING ("d0significance decoration name set, please set decorateTTVVars to True if you want this to be written out"); + } + if (!m_z0sinthetaDecoration.empty()){ + ANA_MSG_WARNING ("z0sintheta decoration name set, please set decorateTTVVars to True if you want this to be written out"); + } + } + ANA_CHECK (m_particlesHandle.initialize (m_systematicsList)); ANA_CHECK (m_preselection.initialize (m_systematicsList, m_particlesHandle, SG::AllowEmpty)); ANA_CHECK (m_selectionHandle.initialize (m_systematicsList, m_particlesHandle)); @@ -72,28 +94,25 @@ namespace CP execute () { SG::ReadHandle<xAOD::EventInfo> eventInfo(m_eventInfoKey); - + SG::ReadHandle<xAOD::VertexContainer> vertices(m_primaryVerticesKey); const xAOD::Vertex *primaryVertex {nullptr}; - if (m_maxDeltaZ0SinTheta > 0) + + for (const xAOD::Vertex *vertex : *vertices) { - SG::ReadHandle<xAOD::VertexContainer> vertices(m_primaryVerticesKey); - for (const xAOD::Vertex *vertex : *vertices) + if (vertex->vertexType() == xAOD::VxType::PriVtx) { - if (vertex->vertexType() == xAOD::VxType::PriVtx) + // The default "PrimaryVertex" container is ordered in + // sum-pt, and the tracking group recommends to pick the one + // with the maximum sum-pt, so this will do the right thing. + // If the user needs a different primary vertex, they need to + // provide a reordered primary vertex container and point + // this algorithm to it. Currently there is no central + // algorithm to do that, so users will have to write their + // own (15 Aug 18). + if (primaryVertex == nullptr) { - // The default "PrimaryVertex" container is ordered in - // sum-pt, and the tracking group recommends to pick the one - // with the maximum sum-pt, so this will do the right thing. - // If the user needs a different primary vertex, he needs to - // provide a reordered primary vertex container and point - // this algorithm to it. Currently there is no central - // algorithm to do that, so users will have to write their - // own (15 Aug 18). - if (primaryVertex == nullptr) - { - primaryVertex = vertex; - break; - } + primaryVertex = vertex; + break; } } } @@ -105,44 +124,38 @@ namespace CP for (const xAOD::IParticle *particle : *particles) { asg::AcceptData acceptData (&m_accept); + float d0sig = -999; + float deltaZ0SinTheta = -999; if (m_preselection.getBool (*particle, sys)) { std::size_t cutIndex {0}; - + const xAOD::TrackParticle *track {nullptr}; - if (const xAOD::Muon *muon = dynamic_cast<const xAOD::Muon *>(particle)) + if (const xAOD::Muon *muon = dynamic_cast<const xAOD::Muon *>(particle)){ track = muon->primaryTrackParticle(); - else if (const xAOD::Electron *electron = dynamic_cast<const xAOD::Electron *>(particle)) + } else if (const xAOD::Electron *electron = dynamic_cast<const xAOD::Electron *>(particle)){ track = electron->trackParticle(); - else - { + } else { ANA_MSG_ERROR ("failed to cast input to electron or muon"); return StatusCode::FAILURE; } acceptData.setCutResult (cutIndex ++, track != nullptr); - if (track != nullptr) - { - if (m_maxD0Significance > 0) - { - try - { - const float d0sig = xAOD::TrackingHelpers::d0significance - (track, eventInfo->beamPosSigmaX(), eventInfo->beamPosSigmaY(), - eventInfo->beamPosSigmaXY()); - acceptData.setCutResult (cutIndex ++, fabs( d0sig ) < m_maxD0Significance); - } catch (const std::runtime_error &) { - acceptData.setCutResult (cutIndex ++, false); - } - } - if (m_maxDeltaZ0SinTheta > 0) - { - const double vertex_z = primaryVertex ? primaryVertex->z() : 0; - const float deltaZ0SinTheta - = (track->z0() + track->vz() - vertex_z) * sin (particle->p4().Theta()); - acceptData.setCutResult (cutIndex ++, fabs (deltaZ0SinTheta) < m_maxDeltaZ0SinTheta); + + if (track != nullptr) { + try { + d0sig = xAOD::TrackingHelpers::d0significance(track, eventInfo->beamPosSigmaX(), eventInfo->beamPosSigmaY(), eventInfo->beamPosSigmaXY()); + if (m_maxD0Significance > 0) acceptData.setCutResult (cutIndex ++, fabs( d0sig ) < m_maxD0Significance); + + } catch (const std::runtime_error &) { + acceptData.setCutResult (cutIndex ++, false); } + + const double vertex_z = primaryVertex ? primaryVertex->z() : 0; + deltaZ0SinTheta = (track->z0() + track->vz() - vertex_z) * sin (particle->p4().Theta()); + if (m_maxDeltaZ0SinTheta > 0) acceptData.setCutResult (cutIndex ++, fabs (deltaZ0SinTheta) < m_maxDeltaZ0SinTheta); + if (m_nMinPixelHits != -1 || m_nMaxPixelHits != -1) { uint8_t nPixelHits; track->summaryValue(nPixelHits, xAOD::numberOfPixelHits); @@ -169,9 +182,11 @@ namespace CP } } } - - m_selectionHandle.setBits - (*particle, selectionFromAccept (acceptData), sys); + if(m_decorateTTVAVars){ + (*m_z0sinthetaDecorator)(*particle) = deltaZ0SinTheta; + (*m_d0sigDecorator)(*particle) = d0sig; + } + m_selectionHandle.setBits(*particle, selectionFromAccept (acceptData), sys); } } diff --git a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisConfig.py b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisConfig.py index 86aac0816d3d..1d902099d65d 100644 --- a/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisConfig.py +++ b/PhysicsAnalysis/Algorithms/EgammaAnalysisAlgorithms/python/ElectronAnalysisConfig.py @@ -207,6 +207,8 @@ class ElectronWorkingPointConfig (ConfigBlock) : self.addOption ('maxDeltaZ0SinTheta', 0.5, type=float, info="maximum z0sinTheta in mm used for the trackSelection" "The default is 0.5 mm") + self.addOption ('writeTrackD0Z0', False, type = bool, + info="save the d0 significance and z0sinTheta variables so they can be written out") self.addOption ('likelihoodWP', None, type=str, info="the ID WP (string) to use. Supported ID WPs: TightLH, " "MediumLH, LooseBLayerLH. ") @@ -255,16 +257,23 @@ class ElectronWorkingPointConfig (ConfigBlock) : postfix = '_' + postfix # Set up the track selection algorithm: - if self.trackSelection : + if self.writeTrackD0Z0 or self.trackSelection : alg = config.createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg', 'ElectronTrackSelectionAlg' + postfix ) alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits' alg.maxD0Significance = self.maxD0Significance - alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta + alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta + alg.decorateTTVAVars = self.writeTrackD0Z0 + alg.d0sigDecoration = 'd0sig' + postfix + alg.z0sinthetaDecoration = 'z0sintheta' + postfix alg.particles = config.readName (self.containerName) alg.preselection = config.getPreselection (self.containerName, '') - config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration) - + if self.trackSelection : + config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration) + if self.writeTrackD0Z0 : + config.addOutputVar (self.containerName, alg.d0sigDecoration, alg.d0sigDecoration,noSys=True) + config.addOutputVar (self.containerName, alg.z0sinthetaDecoration, alg.z0sinthetaDecoration,noSys=True) + if 'LH' in self.likelihoodWP: # Set up the likelihood ID selection algorithm # It is safe to do this before calibration, as the cluster E is used diff --git a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisConfig.py b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisConfig.py index f8afa81b4434..4300efb915b4 100644 --- a/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisConfig.py +++ b/PhysicsAnalysis/Algorithms/MuonAnalysisAlgorithms/python/MuonAnalysisConfig.py @@ -130,6 +130,8 @@ class MuonWorkingPointConfig (ConfigBlock) : self.addOption ('maxDeltaZ0SinTheta', 0.5, type=float, info="maximum Delta z0sinTheta in mm used for the trackSelection" "The default is 0.5 mm") + self.addOption ('writeTrackD0Z0', False, type = bool, + info="save the d0 significance and z0sinTheta variables so they can be written out") self.addOption ('quality', None, type=str, info="the ID WP (string) to use. Supported ID WPs: Tight, Medium, " "Loose, LowPt, HighPt.") @@ -183,15 +185,22 @@ class MuonWorkingPointConfig (ConfigBlock) : postfix = '_' + postfix # Set up the track selection algorithm: - if self.trackSelection : + if self.writeTrackD0Z0 or self.trackSelection: alg = config.createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg', 'MuonTrackSelectionAlg' + postfix ) alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits' + alg.decorateTTVAVars = self.writeTrackD0Z0 + alg.d0sigDecoration = 'd0sig' + postfix + alg.z0sinthetaDecoration = 'z0sintheta' + postfix alg.maxD0Significance = self.maxD0Significance alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta alg.particles = config.readName (self.containerName) alg.preselection = config.getPreselection (self.containerName, '') - config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration, preselection=self.qualitySelectionOutput) + if self.trackSelection : + config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration, preselection=self.qualitySelectionOutput) + if self.writeTrackD0Z0 : + config.addOutputVar (self.containerName, alg.d0sigDecoration, alg.d0sigDecoration,noSys=True) + config.addOutputVar (self.containerName, alg.z0sinthetaDecoration, alg.z0sinthetaDecoration,noSys=True) # Setup the muon quality selection alg = config.createAlgorithm( 'CP::MuonSelectionAlgV2', -- GitLab