From 0319b0c17a01a427a22ffb1bd643f32e66e896ef Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Thu, 25 Sep 2025 17:58:26 +0200 Subject: [PATCH 01/64] First ECAL PF cluster validation sequence --- .../Common/python/HLTValidationHarvest_cff.py | 2 + .../Common/python/HLTValidation_cff.py | 3 + .../python/caloTruthProducer_cfi.py | 12 +- .../RecoParticleFlow/plugins/BuildFile.xml | 3 + .../RecoParticleFlow/plugins/PFTester.cc | 609 +++++++++++++----- .../python/hltPFPostProcessor_cfi.py | 54 ++ .../python/hltPFValidation_cfi.py | 42 ++ .../scripts/makeHLTPFValidationPlots.py | 322 +++++++++ 8 files changed, 879 insertions(+), 168 deletions(-) create mode 100644 Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py create mode 100644 Validation/RecoParticleFlow/python/hltPFValidation_cfi.py create mode 100644 Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py diff --git a/HLTriggerOffline/Common/python/HLTValidationHarvest_cff.py b/HLTriggerOffline/Common/python/HLTValidationHarvest_cff.py index 3adbd44b5ae6f..f1a0a96d336fc 100644 --- a/HLTriggerOffline/Common/python/HLTValidationHarvest_cff.py +++ b/HLTriggerOffline/Common/python/HLTValidationHarvest_cff.py @@ -18,11 +18,13 @@ from Validation.HGCalValidation.HLTHGCalPostProcessor_cff import * from Validation.HLTrigger.HLTGenValidationHarvesting_cff import * from Validation.HGCalValidation.BarrelPostProcessor_cff import * +from Validation.RecoParticleFlow.hltPFPostProcessor_cfi import hltPFClusterPostProcessor hltpostvalidation = cms.Sequence( postProcessorHLTtrackingSequence +postProcessorHLTvertexing +postProcessorHLTvertexingReconstructableSim + +hltPFClusterPostProcessor +HLTMuonPostVal +HLTTauPostVal +EgammaPostVal diff --git a/HLTriggerOffline/Common/python/HLTValidation_cff.py b/HLTriggerOffline/Common/python/HLTValidation_cff.py index 179d38848094e..e822a9d8659a5 100644 --- a/HLTriggerOffline/Common/python/HLTValidation_cff.py +++ b/HLTriggerOffline/Common/python/HLTValidation_cff.py @@ -21,6 +21,8 @@ # Gen-level Validation from Validation.HLTrigger.HLTGenValidation_cff import * from Validation.Configuration.globalValidation_cff import * +# PF +from Validation.RecoParticleFlow.hltPFValidation_cfi import * # HGCAL Rechit Calibration from Validation.HGCalValidation.hgcalHitCalibrationDefault_cfi import hgcalHitCalibrationDefault as _hgcalHitCalibrationDefault @@ -118,6 +120,7 @@ _hltvalidationWithMC_Phase2.insert(-1, hgcalHitCalibrationHLT) _hltvalidationWithMC_Phase2.insert(-1, hltHgcalValidator) _hltvalidationWithMC_Phase2.insert(-1, hltGENValidation) +_hltvalidationWithMC_Phase2.insert(-1, PFValSeq) phase2_common.toReplaceWith(hltvalidationWithMC, _hltvalidationWithMC_Phase2) hltvalidationWithData = cms.Sequence( diff --git a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py index 3a17ea7ba3155..f3c0a2aafd422 100644 --- a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py +++ b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py @@ -68,12 +68,12 @@ ticl_barrel.toModify( caloParticles, simHitCollections = cms.PSet( - hgc = cms.VInputTag( - cms.InputTag('g4SimHits', 'HGCHitsEE'), - cms.InputTag('g4SimHits', 'HGCHitsHEfront'), - cms.InputTag('g4SimHits', 'HGCHitsHEback'), - ), - hcal = cms.VInputTag(cms.InputTag('g4SimHits', 'HcalHits')), + # hgc = cms.VInputTag( + # cms.InputTag('g4SimHits', 'HGCHitsEE'), + # cms.InputTag('g4SimHits', 'HGCHitsHEfront'), + # cms.InputTag('g4SimHits', 'HGCHitsHEback'), + # ), + # hcal = cms.VInputTag(cms.InputTag('g4SimHits', 'HcalHits')), ecal = cms.VInputTag( cms.InputTag('g4SimHits', 'EcalHitsEB') ) diff --git a/Validation/RecoParticleFlow/plugins/BuildFile.xml b/Validation/RecoParticleFlow/plugins/BuildFile.xml index fb6917f8db700..6eea912388131 100644 --- a/Validation/RecoParticleFlow/plugins/BuildFile.xml +++ b/Validation/RecoParticleFlow/plugins/BuildFile.xml @@ -12,6 +12,9 @@ + + + diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 21b1c39969c41..8c759f4a690f8 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -1,222 +1,507 @@ // author: Mike Schmitt, University of Florida // first version 11/7/2007 +#include "FWCore/Framework/interface/MakerMacros.h" +#include "DQMServices/Core/interface/DQMEDAnalyzer.h" #include "DQMServices/Core/interface/DQMStore.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" #include "DataFormats/Common/interface/Handle.h" -#include "DataFormats/Common/interface/RefToBase.h" -#include "DataFormats/HepMCCandidate/interface/GenParticle.h" -#include "DataFormats/METReco/interface/CaloMET.h" -#include "DataFormats/METReco/interface/CaloMETCollection.h" -#include "DataFormats/METReco/interface/GenMET.h" -#include "DataFormats/METReco/interface/GenMETCollection.h" #include "DataFormats/ParticleFlowCandidate/interface/PFCandidate.h" #include "DataFormats/ParticleFlowCandidate/interface/PFCandidateFwd.h" #include "DataFormats/ParticleFlowReco/interface/PFBlock.h" #include "DataFormats/ParticleFlowReco/interface/PFBlockElement.h" #include "DataFormats/ParticleFlowReco/interface/PFRecTrack.h" -#include "FWCore/Framework/interface/one/EDAnalyzer.h" -#include "FWCore/Framework/interface/ESHandle.h" -#include "FWCore/Framework/interface/Event.h" +#include "DataFormats/ParticleFlowReco/interface/PFCluster.h" +#include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" +#include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" +#include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" +#include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" +#include "SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" -#include "FWCore/ParameterSet/interface/ParameterSet.h" -#include "FWCore/ServiceRegistry/interface/Service.h" - -#include #include -#include -#include -#include -#include -#include -#include -#include - -class PFTester : public edm::one::EDAnalyzer<> { -public: - typedef dqm::legacy::DQMStore DQMStore; - typedef dqm::legacy::MonitorElement MonitorElement; - explicit PFTester(const edm::ParameterSet &); - ~PFTester() override; - - void analyze(const edm::Event &, const edm::EventSetup &) override; - void beginJob() override; - void endJob() override; - -private: - // DAQ Tools - DQMStore *dbe_; - std::map me; - - // Inputs from Configuration File - std::string outputFile_; - edm::EDGetTokenT inputPFlowLabel_tok_; +class PFTester : public DQMEDAnalyzer { +public: + explicit PFTester(const edm::ParameterSet&); + +protected: + void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; + void analyze(const edm::Event&, const edm::EventSetup&) override; + + edm::EDGetTokenT PFCandToken_; + edm::EDGetTokenT PFClusterHCALToken_; + edm::EDGetTokenT SimClusterHCALToken_; + edm::EDGetTokenT> RecoToSimAssociatorHCALToken_; + edm::EDGetTokenT> SimToRecoAssociatorHCALToken_; + edm::EDGetTokenT> RecoToCpAssociatorHCALToken_; + edm::EDGetTokenT> CpToRecoAssociatorHCALToken_; + + MonitorElement* h_PFCandEt_; + MonitorElement* h_PFCandEta_; + MonitorElement* h_PFCandPhi_; + MonitorElement* h_PFCandCharge_; + MonitorElement* h_PFCandPdgId_; + MonitorElement* h_PFCandType_; + + MonitorElement* h_NumElements_; + MonitorElement* h_NumTrackElements_; + MonitorElement* h_NumMuonElements_; + MonitorElement* h_NumPS1Elements_; + MonitorElement* h_NumPS2Elements_; + MonitorElement* h_NumECALElements_; + MonitorElement* h_NumHCALElements_; + MonitorElement* h_NumHGCALElements_; + + MonitorElement* h_TrackCharge_; + MonitorElement* h_TrackNumPoints_; + MonitorElement* h_TrackNumMeasurements_; + MonitorElement* h_TrackImpactParameter_; + + MonitorElement* h_NumPFClusters_; + MonitorElement* h_PFClusterE_; + MonitorElement* h_PFClusterEta_; + MonitorElement* h_PFClusterPhi_; + MonitorElement* h_PFClusterDepth_; + MonitorElement* h_PFClusterNHits_; + MonitorElement* h_PFClusterType_; + MonitorElement* h_PFClusterHitFraction_; + MonitorElement* h_PFClusterHitDetId_; + + double assocScoreThreshold_; + + std::unordered_map> histoVars = { + {"Energy", std::make_tuple(100, 0., 50.)}, + {"Pt", std::make_tuple(100, 0., 40.)}, + {"Eta", std::make_tuple(50, -6.5, 6.5)}, + {"Phi", std::make_tuple(50, -3.5, 3.5)}, + {"Mult", std::make_tuple(20, 0., 20.)}, + }; + + using UMap = std::unordered_map; + UMap h_simClusters_; + UMap h_simClustersMatchedRecoClusters_; + UMap h_simClustersMultiMatchedRecoClusters_; + UMap h_recoClusters_; + UMap h_recoClustersMatchedSimClusters_; + UMap h_recoClustersMultiMatchedSimClusters_; }; -#include "FWCore/Framework/interface/MakerMacros.h" -DEFINE_FWK_MODULE(PFTester); - -using namespace edm; -using namespace std; -using namespace reco; +PFTester::PFTester(const edm::ParameterSet& iConfig) + : PFCandToken_(consumes(iConfig.getParameter("PFCand"))), + PFClusterHCALToken_(consumes(iConfig.getParameter("PFClusterHCAL"))), + SimClusterHCALToken_(consumes(iConfig.getParameter("SimClusterHCAL"))), + RecoToSimAssociatorHCALToken_(consumes>( + iConfig.getParameter("PFClusterSimClusterAssociatorHCAL"))), + SimToRecoAssociatorHCALToken_(consumes>( + iConfig.getParameter("PFClusterSimClusterAssociatorHCAL"))), + RecoToCpAssociatorHCALToken_(consumes>( + iConfig.getParameter("PFClusterCaloParticleAssociatorHCAL"))), + CpToRecoAssociatorHCALToken_(consumes>( + iConfig.getParameter("PFClusterCaloParticleAssociatorHCAL"))), + assocScoreThreshold_(iConfig.getParameter("assocScoreThreshold")) {} + +void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { + ibook.setCurrentFolder("HLT/ParticleFlow/PFCandidates"); + h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); + h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); + h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); + h_PFCandCharge_ = ibook.book1D("PFCandCharge", "PFCandCharge", 5, -2, 2); + h_PFCandPdgId_ = ibook.book1D("PFCandPdgId", "PFCandPdgId", 44, -22, 22); + h_PFCandType_ = ibook.book1D("PFCandidateType", "PFCandidateType", 10, 0, 10); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFBlocks"); + h_NumElements_ = ibook.book1D("NumElements", "NumElements", 25, 0, 25); + h_NumTrackElements_ = ibook.book1D("NumTrackElements", "NumTrackElements", 5, 0, 5); + h_NumMuonElements_ = ibook.book1D("NumMuonElements", "NumMuonElements", 5, 0, 5); + h_NumPS1Elements_ = ibook.book1D("NumPS1Elements", "NumPS1Elements", 5, 0, 5); + h_NumPS2Elements_ = ibook.book1D("NumPS2Elements", "NumPS2Elements", 5, 0, 5); + h_NumECALElements_ = ibook.book1D("NumECALElements", "NumECALElements", 5, 0, 5); + h_NumHCALElements_ = ibook.book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); + h_NumHGCALElements_ = ibook.book1D("NumHGCALElements", "NumHGCALElements", 5, 0, 5); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFTracks"); + h_TrackCharge_ = ibook.book1D("TrackCharge", "TrackCharge", 5, -2, 2); + h_TrackNumPoints_ = ibook.book1D("TrackNumPoints", "TrackNumPoints", 100, 0, 100); + h_TrackNumMeasurements_ = ibook.book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); + h_TrackImpactParameter_ = ibook.book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusters"); + h_NumPFClusters_ = ibook.book1D("NumPFClusters", "Number of PFClusters per PFCandidate", 25, 0, 25); + h_PFClusterE_ = ibook.book1D("PFClusterE", "PFCluster Energy;E [GeV]", 100, 0, 100); + h_PFClusterEta_ = ibook.book1D("PFClusterEta", "PFCluster Eta;#eta", 120, -6, 6); + h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "PFCluster Phi;#phi", 128, -3.2, 3.2); + h_PFClusterDepth_ = ibook.book1D("PFClusterDepth", "PFCluster Depth;Depth", 10, 0, 10); + h_PFClusterNHits_ = ibook.book1D("PFClusterNHits", "PFCluster Number of Hits", 100, 0, 100); + h_PFClusterType_ = ibook.book1D("PFClusterEtaWidth", "PFCluster Eta Width;#sigma_{#eta}", 20, 0, 20); + h_PFClusterHitFraction_ = ibook.book1D("PFClusterHitFraction", "PFCluster Hit Fraction;Fraction", 100, 0.0, 1.1); + h_PFClusterHitDetId_ = + ibook.book1D("PFClusterHitDetId", "PFCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation"); + for (auto& hVar : histoVars) { + auto [nBins, hMin, hMax] = hVar.second; + h_simClusters_[hVar.first] = + ibook.book1D("SimClusters" + hVar.first, "SimClusters;" + hVar.first, nBins, hMin, hMax); + h_simClustersMatchedRecoClusters_[hVar.first] = ibook.book1D("SimClustersMatchedRecoClusters" + hVar.first, + "SimClusters matched to RecoClusters;" + hVar.first, + nBins, + hMin, + hMax); + h_simClustersMultiMatchedRecoClusters_[hVar.first] = + ibook.book1D("SimClustersMultiMatchedRecoClusters" + hVar.first, + "SimClusters multi-matched to RecoClusters;" + hVar.first, + nBins, + hMin, + hMax); + + h_recoClusters_[hVar.first] = + ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); + + h_recoClustersMatchedSimClusters_[hVar.first] = ibook.book1D("RecoClustersMatchedSimClusters" + hVar.first, + "RecoClusters matched to SimClusters;" + hVar.first, + nBins, + hMin, + hMax); + h_recoClustersMultiMatchedSimClusters_[hVar.first] = + ibook.book1D("RecoClustersMultiMatchedSimClusters" + hVar.first, + "RecoClusters multi-matched to SimClusters;" + hVar.first, + nBins, + hMin, + hMax); + } +} -PFTester::PFTester(const edm::ParameterSet &iConfig) { - inputPFlowLabel_tok_ = consumes(iConfig.getParameter("InputPFlowLabel")); - outputFile_ = iConfig.getUntrackedParameter("OutputFile"); +void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { + // -------------------------------------------------------------------- + // ---------------- PF Candidates ------------------------------------- + // -------------------------------------------------------------------- - if (!outputFile_.empty()) - edm::LogInfo("OutputInfo") << " ParticleFLow Task histograms will be saved to '" << outputFile_.c_str() << "'"; - else - edm::LogInfo("OutputInfo") << " ParticleFlow Task histograms will NOT be saved"; -} + const reco::PFCandidateCollection* pf_candidates; + edm::Handle PFCand; + iEvent.getByToken(PFCandToken_, PFCand); + if (!PFCand.isValid()) { + edm::LogInfo("PFTester") << "Input PFCand collection not found."; + return; + } -PFTester::~PFTester() {} + pf_candidates = PFCand.product(); + if (!pf_candidates) { + edm::LogInfo("PFTester") << " Failed to retrieve data required by PFTester.cc"; + return; + } -void PFTester::beginJob() { - // get ahold of back-end interface - dbe_ = edm::Service().operator->(); + // -------------------------------------------------------------------- + // ---------------- PF Cluster Efficiency ----------------------------- + // -------------------------------------------------------------------- - if (dbe_) { - dbe_->setCurrentFolder("PFTask/PFCandidates"); + edm::Handle PFClusterHCAL; + iEvent.getByToken(PFClusterHCALToken_, PFClusterHCAL); + if (!PFClusterHCAL.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterHCAL collection not found."; + return; + } + auto recoClusters = *PFClusterHCAL; - me["CandidateEt"] = dbe_->book1D("CandidateEt", "CandidateEt", 1000, 0, 1000); - me["CandidateEta"] = dbe_->book1D("CandidateEta", "CandidateEta", 200, -5, 5); - me["CandidatePhi"] = dbe_->book1D("CandidatePhi", "CandidatePhi", 200, -M_PI, M_PI); - me["CandidateCharge"] = dbe_->book1D("CandidateCharge", "CandidateCharge", 5, -2, 2); - me["PFCandidateType"] = dbe_->book1D("PFCandidateType", "PFCandidateType", 10, 0, 10); + edm::Handle SimClusterHCAL; + iEvent.getByToken(SimClusterHCALToken_, SimClusterHCAL); + if (!SimClusterHCAL.isValid()) { + edm::LogInfo("PFTester") << "Input SimClusterHCAL collection not found."; + return; + } + auto simClusters = *SimClusterHCAL; - dbe_->setCurrentFolder("PFTask/PFBlocks"); + edm::Handle> SimToRecoAssociatorHCALCollection; + iEvent.getByToken(SimToRecoAssociatorHCALToken_, SimToRecoAssociatorHCALCollection); + if (!SimToRecoAssociatorHCALCollection.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociatorHCAL SimToReco collection not found."; + return; + } + auto simToRecoAssoc = *SimToRecoAssociatorHCALCollection; + // std::cout << "simRecColl size : " << simToRecoAssoc.size() << std::endl; - me["NumElements"] = dbe_->book1D("NumElements", "NumElements", 25, 0, 25); - me["NumTrackElements"] = dbe_->book1D("NumTrackElements", "NumTrackElements", 5, 0, 5); - me["NumPS1Elements"] = dbe_->book1D("NumPS1Elements", "NumPS1Elements", 5, 0, 5); - me["NumPS2Elements"] = dbe_->book1D("NumPS2Elements", "NumPS2Elements", 5, 0, 5); - me["NumECALElements"] = dbe_->book1D("NumECALElements", "NumECALElements", 5, 0, 5); - me["NumHCALElements"] = dbe_->book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); - me["NumMuonElements"] = dbe_->book1D("NumMuonElements", "NumMuonElements", 5, 0, 5); + edm::Handle> RecoToSimAssociatorHCALCollection; + iEvent.getByToken(RecoToSimAssociatorHCALToken_, RecoToSimAssociatorHCALCollection); + if (!RecoToSimAssociatorHCALCollection.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociatorHCAL RecoToSim collection not found."; + return; + } + auto recoToSimAssoc = *RecoToSimAssociatorHCALCollection; + + std::vector recoIdsMerged; + + // efficiency and merge rate computation + for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + h_simClusters_["Energy"]->Fill(simClusters[simId].energy()); + h_simClusters_["Pt"]->Fill(simClusters[simId].pt()); + h_simClusters_["Eta"]->Fill(simClusters[simId].eta()); + h_simClusters_["Phi"]->Fill(simClusters[simId].phi()); + h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfSimHits()); + + const edm::Ref simClusterRef(SimClusterHCAL, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + + bool wasNotFilled = true; + for (const auto& recoPair : simToRecoMatched) { + const auto recoPairIdx = recoPair.first.index(); + + if (recoPair.second.second < assocScoreThreshold_) + continue; + + // numerator histograms must be filled only once per sim cluster + if (wasNotFilled) { + wasNotFilled = false; + h_simClustersMatchedRecoClusters_["Energy"]->Fill(simClusters[simId].energy()); + h_simClustersMatchedRecoClusters_["Pt"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersMatchedRecoClusters_["Phi"]->Fill(simClusters[simId].phi()); + h_simClustersMatchedRecoClusters_["Mult"]->Fill(simClusters[simId].numberOfSimHits()); + } - dbe_->setCurrentFolder("PFTask/PFTracks"); + // discard reco clusters from merge counting if already considered for a previous sim cluster + const auto& mergeIt = std::find(recoIdsMerged.begin(), recoIdsMerged.end(), recoPairIdx); + if (mergeIt != recoIdsMerged.end()) + continue; + recoIdsMerged.push_back(recoPairIdx); + + const edm::Ref recoClusterRef(PFClusterHCAL, recoPairIdx); + const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); + assert(recoToSimIt != recoToSimAssoc.end()); + const auto& recoToSimMatched = recoToSimIt->val; + assert(!recoToSimMatched.empty()); + + // find how many reco clusters are associated to the matched sim cluster + unsigned nSimMerged = 0; + for (const auto& simPair : recoToSimMatched) { + if (simPair.second < assocScoreThreshold_) + continue; + ++nSimMerged; + } - me["TrackCharge"] = dbe_->book1D("TrackCharge", "TrackCharge", 5, -2, 2); - me["TrackNumPoints"] = dbe_->book1D("TrackNumPoints", "TrackNumPoints", 100, 0, 100); - me["TrackNumMeasurements"] = dbe_->book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); - me["TrackImpactParameter"] = dbe_->book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); + if (nSimMerged > 1) { + h_simClustersMultiMatchedRecoClusters_["Energy"]->Fill(recoClusters[simId].energy()); + h_simClustersMultiMatchedRecoClusters_["Pt"]->Fill(recoClusters[simId].pt()); + h_simClustersMultiMatchedRecoClusters_["Eta"]->Fill(recoClusters[simId].eta()); + h_simClustersMultiMatchedRecoClusters_["Phi"]->Fill(recoClusters[simId].phi()); + h_simClustersMultiMatchedRecoClusters_["Mult"]->Fill(recoClusters[simId].size()); + } - dbe_->setCurrentFolder("PFTask/PFClusters"); + // for (const auto& recoPair : simToRecoMatched) { + // std::cout << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() + // << " shared energy = " << recoPair.second.first + // << " score = " << recoPair.second.second << std::endl; + // } + } } -} -void PFTester::analyze(const edm::Event &iEvent, const edm::EventSetup &iSetup) { - // Data to Retrieve from the Event - const PFCandidateCollection *pflow_candidates; + std::vector simIdsDuplicates; + + // fake rate and duplicate computation + for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { + h_recoClusters_["Energy"]->Fill(recoClusters[recoId].energy()); + h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); + + const edm::Ref recoClusterRef(PFClusterHCAL, recoId); + const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); + if (recoToSimIt == recoToSimAssoc.end()) + continue; + const auto& recoToSimMatched = recoToSimIt->val; + if (recoToSimMatched.empty()) + continue; + + bool wasNotFilled = true; + for (const auto& simPair : recoToSimMatched) { + const auto simPairIdx = simPair.first.index(); + // std::cout << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx + // << " score = " << simPair.second << std::endl; + + if (simPair.second < assocScoreThreshold_) + continue; + + // numerator histograms must be filled only once per reco cluster + if (wasNotFilled) { + wasNotFilled = false; + h_recoClustersMatchedSimClusters_["Energy"]->Fill(recoClusters[recoId].energy()); + h_recoClustersMatchedSimClusters_["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMatchedSimClusters_["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClustersMatchedSimClusters_["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClustersMatchedSimClusters_["Mult"]->Fill(recoClusters[recoId].size()); + } - // ========================================================== - // Retrieve! - // ========================================================== + // discard sim clusters from duplicate counting if already considered for a previous reco cluster + const auto& dupIt = std::find(simIdsDuplicates.begin(), simIdsDuplicates.end(), simPairIdx); + if (dupIt != simIdsDuplicates.end()) + continue; + simIdsDuplicates.push_back(simPairIdx); + + const edm::Ref simClusterRef(SimClusterHCAL, simPairIdx); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + assert(simToRecoIt != simToRecoAssoc.end()); + const auto& simToRecoMatched = simToRecoIt->val; + assert(!simToRecoMatched.empty()); + + // find how many reco clusters are associated to the matched sim cluster + unsigned nRecoDuplicates = 0; + for (const auto& recoPair : simToRecoMatched) { + if (recoPair.second.second < assocScoreThreshold_) + continue; + ++nRecoDuplicates; + } - { - // Get Particle Flow Candidates - Handle pflow_hnd; - iEvent.getByToken(inputPFlowLabel_tok_, pflow_hnd); - pflow_candidates = pflow_hnd.product(); + if (nRecoDuplicates > 1) { + h_recoClustersMultiMatchedSimClusters_["Energy"]->Fill(recoClusters[recoId].energy()); + h_recoClustersMultiMatchedSimClusters_["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMultiMatchedSimClusters_["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClustersMultiMatchedSimClusters_["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClustersMultiMatchedSimClusters_["Mult"]->Fill(recoClusters[recoId].size()); + } + } } - if (!pflow_candidates) { - edm::LogInfo("OutputInfo") << " failed to retrieve data required by ParticleFlow Task"; - edm::LogInfo("OutputInfo") << " ParticleFlow Task cannot continue...!"; - return; + // -------------------------------------------------------------------- + // -------------------------------------------------------------------- + + edm::Handle> RecoToCpAssociatorHCALCollection; + iEvent.getByToken(RecoToCpAssociatorHCALToken_, RecoToCpAssociatorHCALCollection); + if (!RecoToCpAssociatorHCALCollection.isValid()) { + std::cout << "Input PFClusterCpClusterAssociatorHCAL RecoToSim collection not found." << std::endl; + edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociatorHCAL RecoToSim collection not found."; + } else { + auto recCpColl = *RecoToCpAssociatorHCALCollection; + // std::cout << "recCpColl size : " << recCpColl.size() << std::endl; + + for (unsigned int cId = 0; cId < recoClusters.size(); ++cId) { + const edm::Ref clusterRef(PFClusterHCAL, cId); + const auto& scsIt = recCpColl.find(clusterRef); + if (scsIt == recCpColl.end()) + continue; + // const auto& scs = scsIt->val; + // if (!scs.empty()) { + // for (const auto& scPair : scs) { + // // std::cout << " recCpColl Cluster id " << cId << " : first=" << scPair.first.index() + // // << " second=" << scPair.second << std::endl; + // } + // } + } } - // ========================================================== - // Analyze! - // ========================================================== + edm::Handle> CpToRecoAssociatorHCALCollection; + iEvent.getByToken(CpToRecoAssociatorHCALToken_, CpToRecoAssociatorHCALCollection); + if (!CpToRecoAssociatorHCALCollection.isValid()) { + std::cout << "Input PFClusterCpClusterAssociatorHCAL SimToReco collection not found." << std::endl; + edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociatorHCAL SimToReco collection not found."; + } else { + auto CpRecColl = *CpToRecoAssociatorHCALCollection; + // std::cout << "CpRecColl size : " << CpRecColl.size() << std::endl; + } - // Loop Over Particle Flow Candidates - PFCandidateCollection::const_iterator pf; - for (pf = pflow_candidates->begin(); pf != pflow_candidates->end(); pf++) { - const PFCandidate *particle = &(*pf); + // -------------------------------------------------------------------- + // -------------------- PF Blocks and Elements ------------------------ + // -------------------------------------------------------------------- - // Fill Histograms for Candidate Methods - me["CandidateEt"]->Fill(particle->et()); - me["CandidateEta"]->Fill(particle->eta()); - me["CandidatePhi"]->Fill(particle->phi()); - me["CandidateCharge"]->Fill(particle->charge()); - me["CandidatePdgId"]->Fill(particle->pdgId()); + // Loop Over Particle Flow Candidates + for (size_t i = 0; i < pf_candidates->size(); ++i) { + const auto& particle = (*pf_candidates)[i]; - // Fill Histograms for PFCandidate Specific Methods - me["PFCandidateType"]->Fill(particle->particleId()); - // particle->elementsInBlocks(); + h_PFCandEt_->Fill(particle.et()); + h_PFCandEta_->Fill(particle.eta()); + h_PFCandPhi_->Fill(particle.phi()); + h_PFCandCharge_->Fill(particle.charge()); + h_PFCandPdgId_->Fill(particle.pdgId()); + h_PFCandType_->Fill(particle.particleId()); // Get the PFBlock and Elements - // JW: Returns vector of blocks now ,TO BE FIXED ---- - /*PFBlock block = *(particle->block()); - OwnVector elements = block.elements(); - int numElements = elements.size(); + const reco::PFCandidate::ElementsInBlocks& elementsInBlocks = particle.elementsInBlocks(); + int numElements = elementsInBlocks.size(); int numTrackElements = 0; + int numMuonElements = 0; int numPS1Elements = 0; int numPS2Elements = 0; int numECALElements = 0; int numHCALElements = 0; - int numMuonElements = 0; + int numHGCALElements = 0; + int numPFClusters = 0; // Loop over Elements in Block - OwnVector::const_iterator element; - for (element = elements.begin(); element != elements.end(); element++) { + for (const auto& elemBlockPair : elementsInBlocks) { + reco::PFBlockRef blockRef = elemBlockPair.first; + unsigned elementIndex = elemBlockPair.second; + const reco::PFBlockElement& element = blockRef->elements()[elementIndex]; + int element_type = element.type(); - int element_type = element->type(); // Element is a Tracker Track - if (element_type == PFBlockElement::TRACK) { - + if (element_type == reco::PFBlockElement::TRACK) { // Get General Information about the Track - PFRecTrack track = *(element->trackRefPF()); - me["TrackCharge"]->Fill(track.charge()); - me["TrackNumPoints"]->Fill(track.nTrajectoryPoints()); - me["TrackNumMeasurements"]->Fill(track.nTrajectoryMeasurements()); + reco::PFRecTrack track = *(element.trackRefPF()); + h_TrackCharge_->Fill(track.charge()); + h_TrackNumPoints_->Fill(track.nTrajectoryPoints()); + h_TrackNumMeasurements_->Fill(track.nTrajectoryMeasurements()); // Loop Over Points in the Track - vector points = track.trajectoryPoints(); - vector::iterator point; + std::vector points = track.trajectoryPoints(); + std::vector::iterator point; for (point = points.begin(); point != points.end(); point++) { int point_layer = point->layer(); - double x = point->positionXYZ().x(); - double y = point->positionXYZ().y(); - double z = point->positionXYZ().z(); - //switch (point_layer) { - //case PFTrajectoryPoint::ClosestApproach: - // Fill the Track's D0 - if (point_layer == PFTrajectoryPoint::ClosestApproach) { - me["TrackImpactParameter"]->Fill(sqrt(x*x + y*y + z*z)); + double x = point->position().x(); + double y = point->position().y(); + double z = point->position().z(); + if (point_layer == reco::PFTrajectoryPoint::ClosestApproach) { + h_TrackImpactParameter_->Fill(sqrt(x * x + y * y + z * z)); // [FIXME] } } numTrackElements++; + } else if (element_type == reco::PFBlockElement::MUON) { + numMuonElements++; // Element is a Muon Track + } else { + if (element_type == reco::PFBlockElement::PS1) + numPS1Elements++; // Element is a PreShower1 Cluster + if (element_type == reco::PFBlockElement::PS2) + numPS2Elements++; // Element is a PreShower2 Cluster + if (element_type == reco::PFBlockElement::ECAL) + numECALElements++; // Element is an ECAL Cluster + if (element_type == reco::PFBlockElement::HCAL) + numHCALElements++; // Element is a HCAL Cluster + if (element_type == reco::PFBlockElement::HGCAL) + numHGCALElements++; // Element is a HGCAL Cluster + + if (element.clusterRef().isNonnull()) { + auto const& cluster = *(element.clusterRef()); + numPFClusters++; + h_PFClusterE_->Fill(cluster.energy()); + h_PFClusterEta_->Fill(cluster.eta()); + h_PFClusterPhi_->Fill(cluster.phi()); + h_PFClusterDepth_->Fill(cluster.depth()); + h_PFClusterNHits_->Fill(cluster.recHitFractions().size()); + h_PFClusterType_->Fill(element_type); + for (const auto& hitFracPair : cluster.hitsAndFractions()) { + DetId hitId = hitFracPair.first; + float fraction = hitFracPair.second; + h_PFClusterHitFraction_->Fill(fraction); + h_PFClusterHitDetId_->Fill(hitId.rawId() % 10000); // modulo for visualization + } + } } - - // Element is an ECAL Cluster - else if (element_type == PFBlockElement::ECAL) { - numECALElements++; - } - // Element is a HCAL Cluster - else if (element_type == PFBlockElement::HCAL) { - numHCALElements++; - } - // Element is a Muon Track - else if (element_type == PFBlockElement::MUON) { - numMuonElements++; - } - // Fill the Respective Elements Sizes - me["NumElements"]->Fill(numElements); - me["NumTrackElements"]->Fill(numTrackElements); - me["NumPS1Elements"]->Fill(numPS1Elements); - me["NumPS2Elements"]->Fill(numPS2Elements); - me["NumECALElements"]->Fill(numECALElements); - me["NumHCALElements"]->Fill(numHCALElements); - me["NumMuonElements"]->Fill(numMuonElements); - } ---------------------------------------------- */ + } + + // Fill the Respective Elements Sizes + h_NumElements_->Fill(numElements); + h_NumPFClusters_->Fill(numPFClusters); + h_NumTrackElements_->Fill(numTrackElements); + h_NumMuonElements_->Fill(numMuonElements); + h_NumPS1Elements_->Fill(numPS1Elements); + h_NumPS2Elements_->Fill(numPS2Elements); + h_NumECALElements_->Fill(numECALElements); + h_NumHCALElements_->Fill(numHCALElements); + h_NumHGCALElements_->Fill(numHGCALElements); } } -void PFTester::endJob() { - // Store the DAQ Histograms - if (!outputFile_.empty() && dbe_) - dbe_->save(outputFile_); -} +DEFINE_FWK_MODULE(PFTester); diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py new file mode 100644 index 0000000000000..6086545bff864 --- /dev/null +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -0,0 +1,54 @@ +import FWCore.ParameterSet.Config as cms +from DQMServices.Core.DQMEDHarvester import DQMEDHarvester + +hltPFClusterPostProcessor = DQMEDHarvester( + "DQMGenericClient", + subDirs=cms.untracked.vstring("HLT/ParticleFlow/PFClusterValidation"), + efficiency = cms.vstring( + "Eff_vs_EnergyEta 'Efficiency vs Energy-#eta' SimClustersMatchedRecoClustersEnergy_Eta SimClustersEnergy_Eta", + "Eff_vs_EnergyPhi 'Efficiency vs Energy-#phi' SimClustersMatchedRecoClustersEnergy_Phi SimClustersEnergy_Phi", + "Eff_vs_EnergyMult 'Efficiency vs Energy-Mult' SimClustersMatchedRecoClustersEnergy_Mult SimClustersEnergy_Mult", + "Eff_vs_PtEta 'Efficiency vs p_{T}-#eta' SimClustersMatchedRecoClustersPt_Eta SimClustersPt_Eta", + "Eff_vs_PtPhi 'Efficiency vs p_{T}-#phi' SimClustersMatchedRecoClustersPt_Phi SimClustersPt_Phi", + "Eff_vs_PtMult 'Efficiency vs p_{T}-Mult' SimClustersMatchedRecoClustersPt_Mult SimClustersPt_Mult", + "Eff_vs_MultEta 'Efficiency vs Mult-#eta' SimClustersMatchedRecoClustersMult_Eta SimClustersMult_Eta", + "Eff_vs_MultPhi 'Efficiency vs Mult-#phi' SimClustersMatchedRecoClustersMult_Phi SimClustersMult_Phi", + "Fake_vs_EnergyEta 'Fake Rate vs Energy-#eta' RecoClustersMatchedSimClustersEnergy_Eta RecoClustersPt_Eta fake", + "Fake_vs_EnergyPhi 'Fake Rate vs Energy-#phi' RecoClustersMatchedSimClustersEnergy_Phi RecoClustersPt_Phi fake", + "Fake_vs_EnergyMult 'Fake Rate vs Energy-Mult' RecoClustersMatchedSimClustersEnergy_Mult RecoClustersPt_Mult fake", + "Fake_vs_PtEta 'Fake Rate vs p_{T}-#eta' RecoClustersMatchedSimClustersPt_Eta RecoClustersPt_Eta fake", + "Fake_vs_PtPhi 'Fake Rate vs p_{T}-#phi' RecoClustersMatchedSimClustersPt_Phi RecoClustersPt_Phi fake", + "Fake_vs_PtMult 'Fake Rate vs p_{T}-Mult' RecoClustersMatchedSimClustersPt_Mult RecoClustersPt_Mult fake", + "Fake_vs_MultEta 'Fake Rate vs Mult-#eta' RecoClustersMatchedSimClustersMult_Eta RecoClustersMult_Eta fake", + "Fake_vs_MultPhi 'Fake Rate vs Mult-#phi' RecoClustersMatchedSimClustersMult_Phi RecoClustersMult_Phi fake", + ), + efficiencyProfile = cms.untracked.vstring( # for smoother rebinning + # Efficiency + "Eff_vs_Energy 'Efficiency vs Energy' SimClustersMatchedRecoClustersEnergy SimClustersEnergy ", + "Eff_vs_Pt 'Efficiency vs p_{T}' SimClustersMatchedRecoClustersPt SimClustersPt ", + "Eff_vs_Eta 'Efficiency vs #eta' SimClustersMatchedRecoClustersEta SimClustersEta ", + "Eff_vs_Phi 'Efficiency vs #phi' SimClustersMatchedRecoClustersPhi SimClustersPhi ", + "Eff_vs_Mult 'Efficiency vs Multiplicity' SimClustersMatchedRecoClustersMult SimClustersMult ", + # Fake rate + "Fake_vs_Energy 'Fake Rate vs Energy' RecoClustersMatchedSimClustersEnergy RecoClustersEnergy ", + "Fake_vs_Pt 'Fake Rate vs p_{T}' RecoClustersMatchedSimClustersPt RecoClustersPt ", + "Fake_vs_Eta 'Fake Rate vs #eta' RecoClustersMatchedSimClustersEta RecoClustersEta ", + "Fake_vs_Phi 'Fake Rate vs #phi' RecoClustersMatchedSimClustersPhi RecoClustersPhi ", + "Fake_vs_Mult 'Fake Rate vs Multiplicity' RecoClustersMatchedSimClustersMult RecoClustersMult ", + # Duplicate rate + "Dup_vs_Energy 'Dup Rate vs Energy' RecoClustersMultiMatchedSimClustersEnergy RecoClustersEnergy ", + "Dup_vs_Pt 'Dup Rate vs p_{T}' RecoClustersMultiMatchedSimClustersPt RecoClustersPt ", + "Dup_vs_Eta 'Dup Rate vs #eta' RecoClustersMultiMatchedSimClustersEta RecoClustersEta ", + "Dup_vs_Phi 'Dup Rate vs #phi' RecoClustersMultiMatchedSimClustersPhi RecoClustersPhi ", + "Dup_vs_Mult 'Dup Rate vs Mult' RecoClustersMultiMatchedSimClustersMult RecoClustersMult ", + # Merge rate + "Merge_vs_Energy 'Merge Rate vs Energy' SimClustersMultiMatchedRecoClustersEnergy SimClustersEnergy ", + "Merge_vs_Pt 'Merge Rate vs p_{T}' SimClustersMultiMatchedRecoClustersPt SimClustersPt ", + "Merge_vs_Eta 'Merge Rate vs #eta' SimClustersMultiMatchedRecoClustersEta SimClustersEta ", + "Merge_vs_Phi 'Merge Rate vs #phi' SimClustersMultiMatchedRecoClustersPhi SimClustersPhi ", + "Merge_vs_Mult 'Merge Rate vs Multiplicity' SimClustersMultiMatchedRecoClustersMult SimClustersMult ", + ), + resolution = cms.vstring(), + verbose = cms.untracked.uint32(2), + outputFileName = cms.untracked.string("") +) diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py new file mode 100644 index 0000000000000..e23acf5adda75 --- /dev/null +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -0,0 +1,42 @@ +import FWCore.ParameterSet.Config as cms + +hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", + hardScatterOnly = cms.bool(True), + hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), + hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), # hltParticleFlowClusterHO +) + +hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", + associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), + label_lcl = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + label_scl = cms.InputTag("mix","MergedCaloTruth") +) + +hltPFCpAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToCPAssociatorByEnergyScoreProducer", + hardScatterOnly = cms.bool(True), + hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), + hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), # hltParticleFlowClusterHO +) + +hltPFClusterCaloParticleAssociationProducerECAL = cms.EDProducer("PCToCPAssociatorEDProducer", + associator = cms.InputTag("hltPFCpAssocByEnergyScoreProducer"), + label_lc = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + label_cp = cms.InputTag("mix","MergedCaloTruth") +) + +hltPFTesterECAL = cms.EDProducer("PFTester", + PFCand = cms.InputTag("hltParticleFlowTmp"), + PFClusterHCAL = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + SimClusterHCAL = cms.InputTag("mix","MergedCaloTruth"), + PFClusterSimClusterAssociatorHCAL = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), + PFClusterCaloParticleAssociatorHCAL = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + assocScoreThreshold = cms.double(0.) +) + +PFValSeq = cms.Sequence( + hltPFScAssocByEnergyScoreProducer + +hltPFClusterSimClusterAssociationProducerECAL + +hltPFCpAssocByEnergyScoreProducer + +hltPFClusterCaloParticleAssociationProducerECAL + +hltPFTesterECAL +) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py new file mode 100644 index 0000000000000..b2f4483f5d614 --- /dev/null +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 + +import os +import argparse +import numpy as np +import hist +import matplotlib.pyplot as plt +from matplotlib.transforms import blended_transform_factory +plt.style.use('tableau-colorblind10') +import array +import ROOT +import mplhep as hep +hep.style.use("CMS") + +import warnings +warnings.filterwarnings("ignore", message="The value of the smallest subnormal") + +class dotdict(dict): + """dot.notation access to dictionary attributes""" + __getattr__ = dict.get + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ + +def createDir(adir): + if not os.path.exists(adir): + os.makedirs(adir) + return adir + +def checkRootDir(afile, adir): + if not afile.Get(adir): + raise RuntimeError(f"Directory '{adir}' not found in {afile}") + +def checkRootFile(afile, hname, rebin=None): + hist_orig = afile.Get(hname) + if not hist_orig: + raise RuntimeError(f"WARNING: Histogram {hname} not found.") + + hist = hist_orig.Clone(hname + "_clone") + hist.SetDirectory(0) # detach from file + + if rebin is not None: + if isinstance(rebin, (int, float)): + hist = hist.Rebin(int(rebin), hname + "_rebin") + elif hasattr(rebin, '__iter__'): + bin_edges_c = array.array('d', rebin) + hist = hist.Rebin(len(bin_edges_c) - 1, hname + "_rebin", bin_edges_c) + else: + raise ValueError(f"Unknown type for rebin: {type(rebin)}") + + return hist + + +def rate_errorbar_declutter(eff, err, yaxmin, frac=0.01): + """ + Filter uncertainties if they lie below the minimum (vertical) axis value. + Used to plot the filtered points differently, for instance by displaying only the upper uncertainty. + Uncertainties are trimmed to 1. or 0. if these values are crossed + (the retrieved Clopper-Pearson uncertainties have been symmetrized in the DQMGenericClient). + """ + filt = eff-err/2 <= yaxmin + eff_filt = np.where(filt, np.nan, eff) + err_filt = np.where(filt, np.nan, err) + + up_error = eff_filt + err_filt/2 + transform = blended_transform_factory(plotter.ax.transData, plotter.ax.transAxes) + up_error = np.where(np.isnan(up_error), frac, up_error) # place at 0.9% above the minimum vertical axis value + up_error = np.where(up_error != frac, np.nan, up_error) + + filt_limit_one = eff_filt+err_filt/2 > 1. + filt_limit_zero = eff_filt-err_filt/2 < 0. + err_hi = np.where(filt_limit_one, 0., err_filt) + err_lo = np.where(filt_limit_zero, 0., err_filt) + return eff_filt, (err_lo/2,err_hi/2), up_error, transform + +def define_bins(h): + """ + Computes the number of bins, edges, centers and widths of a histogram. + """ + N = h.GetNbinsX() + edges = np.array([h.GetBinLowEdge(i+1) for i in range(N)]) + edges = np.append(edges, h.GetBinLowEdge(N+1)) + return N, edges, 0.5*(edges[:-1]+edges[1:]), np.diff(edges) + +def histo_values_errors(h): + N = h.GetNbinsX() + values = np.array([h.GetBinContent(i+1) for i in range(N)]) + errors = np.array([h.GetBinError(i+1) for i in range(N)]) + return values, errors + +class Plotter: + def __init__(self, label, fontsize=18, grid_color='grey'): + self._fig, self._ax = plt.subplots(figsize=(10, 10)) + self.fontsize = fontsize + + hep.cms.text(' Phase-2 Simulation Preliminary', ax=self._ax, fontsize=fontsize) + hep.cms.lumitext(label + " | 14 TeV", ax=self._ax, fontsize=fontsize) + if grid_color: + self._ax.grid(which='major', color=grid_color) + + self.extensions = ('png', 'pdf') + + @property + def fig(self): + return self._fig + + @property + def ax(self): + return self._ax + + def labels(self, x, y, legend_title=None, legend_loc='upper right'): + self._ax.set_xlabel(x) + self._ax.set_ylabel(y) + if legend_title is not None: + self._ax.legend(title=legend_title, + title_fontsize=self.fontsize, fontsize=self.fontsize, + loc=legend_loc) + + def limits(self, x=None, y=None, logY=False, logX=False): + if x: + self._ax.set_xlim(x) + if y: + self._ax.set_ylim(y) + if logX: + self._ax.set_xscale('log') + if logY: + self._ax.set_yscale('log') + + def limits_with_margin(self, mdValues, mdErrors, logY=False, logX=False): + amin, amax = float('inf'), float('-inf') + + if not isinstance(mdValues, (list,tuple)): + mdValues = [mdValues] + mdErrors = [mdErrors] + + for values, errors in zip(mdValues, mdErrors): + values[np.isinf(values) | np.isnan(values)] = 0. + errors[np.isinf(errors) | np.isnan(errors)] = 0. + + up = values + errors + do = values - errors + + diff_step = 0.05 * abs(up.max()-do.min()) + if logY: + amin = min(amin, (max(values.min(), 1e-5))) + amax = max(amax, up.max() + 10*diff_step) + else: + amin = min(amin, do.min() - diff_step) + amax = max(amax, up.max() + 2*diff_step) + + self.limits(y=(amin,amax), logY=logY) + + def save(self, name): + for ext in self.extensions: + print(" ### INFO: Saving " + name + '.' + ext) + plt.savefig(name + '.' + ext) + plt.close() + +def plotEffComp1D(afile, adir, vars1d, outdir, text, top_text=False): + """ + Plots 1D distributions. + The `avars` variables is a dictionary whose values are (xlabel, ylabel, rebin). + """ + plotter = Plotter(args.sample_label, grid_color=None) + ax2 = plotter.ax.twinx() + eff_color = '#bd1f01' + ax2.set_ylabel('Efficiency', color=eff_color) + + valuesList, errorsList = [], [] + colors_iter = iter(('black', 'blue')) + for avar in vars1d: + name, (xlabel, _, rebin, logy, leglabel) = avar + + root_hist = checkRootFile(afile, f"{adir}/{name}", rebin=rebin) + nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) + values, errors = histo_values_errors(root_hist) + errors /= 2 + + # normalization + errors /= sum(values) + values /= sum(values) + + if 'Eff' in name: + # ax2.set_yscale('log') + ax2.set_ylim(-0.05, 1.15) + + # eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(eff_values, eff_errors, axmin) + ax2.stairs(values, bin_edges, linewidth=2, baseline=None, color=eff_color) + ax2.errorbar(bin_centers, values, xerr=None, yerr=errors, + color=eff_color, fmt='s', label=leglabel, **errorbar_kwargs) + + else: + line = plotter.ax.stairs(values, bin_edges, linewidth=2, + baseline=None, color=next(colors_iter)) + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, + color=line.get_edgecolor(), + fmt='s', label=leglabel, **errorbar_kwargs) + + valuesList.append(values) + errorsList.append(errors) + + plotter.limits_with_margin(valuesList, errorsList, logY=logy) + plotter.labels(x=xlabel, y='[a.u.]', legend_title='') + + plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, + verticalalignment='top', horizontalalignment='left') + + if top_text: + plotter.ax.text(0.97, 0.97, root_hist.GetTitle().replace('ET', r'$E_T$'), + transform=plotter.ax.transAxes, fontsize=fontsize, + verticalalignment='top', horizontalalignment='right') + + ax2.grid(color=eff_color, axis='y') + ax2.tick_params(axis='y', labelcolor=eff_color) + plotter.ax.grid(color=eff_color, axis='x') + + plt.tight_layout() + plotter.save( os.path.join(outdir, name + 'Comp') ) + +def plot1Dvars(afile, adir, avars, outdir, text, top_text=False): + for var, (xlabel, ylabel, rebin, logy, _) in avars.items(): + plotter = Plotter(args.sample_label) + root_hist = checkRootFile(afile, f"{adir}/{var}", rebin=rebin) + nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) + values, errors = histo_values_errors(root_hist) + errors /= 2 + + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, + fmt='s', color='black', label=var, **errorbar_kwargs) + plotter.ax.stairs(values, bin_edges, color='black', linewidth=2, baseline=None) + + plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, + verticalalignment='top', horizontalalignment='left') + + plotter.limits_with_margin(values, errors, logY=logy) + plotter.labels(x=xlabel, y=ylabel) + + if top_text: + plotter.ax.text(0.97, 0.97, root_hist.GetTitle().replace('ET', r'$E_T$'), + transform=plotter.ax.transAxes, fontsize=fontsize, + verticalalignment='top', horizontalalignment='right') + + plt.tight_layout() + plotter.save( os.path.join(outdir, var) ) + + +if __name__ == '__main__': + + def check_list_length(value): + if len(value) <= 1: + raise argparse.ArgumentTypeError("List must have more than one item") + return value + + class DependencyAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + if option_string == "--compare_files_labels" and not namespace.compare_files: + parser.error("`--compare_files_labels` requires `--compare_files` to be set") + if option_string == "--compare_files_labels" and not namespace.met: + parser.error("`--compare_files_labels` requires `--met` to be set") + if len(namespace.met) > 1: + parser.error("When comparing files only one MET collection can be used.") + if len(values) != len(namespace.compare_files): + parser.error("`--compare_files_labels` must have the same size as `--compare_files`") + setattr(namespace, self.dest, values) + + full_command = 'python3 Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py --odir -l TTbar -f ' + parser = argparse.ArgumentParser(description='Make HLT PF validation plots. \nExample command:\n' + full_command) + parser.add_argument('-o', '--odir', default="HLTPFValidationPlots", help='Path to the output directory.') + parser.add_argument('-l', '--sample_label', default="QCD (200 PU)", help='Sample label for plotting.') + + mutual_excl2 = parser.add_mutually_exclusive_group(required=True) + mutual_excl2.add_argument('-f', '--file', help='Paths to the DQM ROOT file.') + mutual_excl2.add_argument('-x', '--compare_files', nargs='+', type=check_list_length, + help='Compare the same collection in different DQM files.', ) + parser.add_argument('-y', '--compare_files_labels', nargs='+', + action=DependencyAction, help='Compare the same collection in different DQM files.',) + + args = parser.parse_args() + + createDir(args.odir) + + fontsize = 16 + colors = hep.style.CMS['axes.prop_cycle'].by_key()['color'] + markers = ('o', 's', 'd') + errorbar_kwargs = dict(capsize=3, elinewidth=0.8, capthick=2, linewidth=2, linestyle='') + + nEventsLabel = '# Events' + effLabel = 'Efficiency' + vars1D = { + # PF tester producer + **{x + 'ClustersEnergy': ('Energy [GeV]', nEventsLabel, None, True, x) for x in ('Sim', 'Reco')}, + 'Eff_vs_Energy': ('Energy [GeV]', effLabel, None, True, None), + **{x + 'ClustersPt': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, x) for x in ('Sim', 'Reco')}, + 'Eff_vs_Pt': (r'$p_{T}$ [GeV]', effLabel, None, False, None), + **{x + 'ClustersEta': (r'$\eta$', nEventsLabel, None, False, x) for x in ('Sim', 'Reco')}, + 'Eff_vs_Eta': (r'$\eta$', effLabel, None, True, None), + **{x + 'ClustersPhi': (r'$\phi$', nEventsLabel, None, False, x) for x in ('Sim', 'Reco')}, + 'Eff_vs_Phi': (r'$\phi$', effLabel, None, True, None), + **{x + 'ClustersMult': ('Multiplicity', nEventsLabel, None, True, x) for x in ('Sim', 'Reco')}, + 'Eff_vs_Mult': ('Multiplicity', effLabel, None, False, None), + } + + dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation" + if args.compare_files is not None: + for afile, alabel in zip(args.compare_files, args.compare_files_labels): + afile = ROOT.TFile.Open(afile) + checkRootDir(afile, dqm_dir) + plot1DFilesComparison(dqm_dir, vars1D, outdir=args.odir, text='', + files=args.compare_files, + files_labels=args.compare_files_labels) + + else: + afile = ROOT.TFile.Open(args.file) + + # Plot 1D PF variables + checkRootDir(afile, dqm_dir) + # plot1Dvars(afile, dqm_dir, vars1D, outdir=args.odir, text='') + + # Compare pairs of variables + it = iter(vars1D.items()) + for var in it: + avars = (var, next(it), next(it)) # reco, sim and efficiency for a given variable + plotEffComp1D(afile, dqm_dir, vars1d=avars, outdir=args.odir, text='') From b838a862beec4ad5842b636d3126aec3f0a4af35 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 17 Oct 2025 17:20:18 +0200 Subject: [PATCH 02/64] Scan over associator score threshold, add reconstructable histograms, cut on simTrack in barrel --- .../RecoParticleFlow/plugins/PFTester.cc | 653 +++++++++++++----- .../python/hltPFPostProcessor_cfi.py | 107 +-- .../python/hltPFValidation_cfi.py | 10 +- .../scripts/makeHLTPFValidationPlots.py | 211 ++++-- 4 files changed, 729 insertions(+), 252 deletions(-) mode change 100644 => 100755 Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 8c759f4a690f8..d63abe20f67a0 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -13,7 +13,6 @@ #include "DataFormats/ParticleFlowReco/interface/PFBlockElement.h" #include "DataFormats/ParticleFlowReco/interface/PFRecTrack.h" #include "DataFormats/ParticleFlowReco/interface/PFCluster.h" -#include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" #include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" #include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" @@ -28,14 +27,15 @@ class PFTester : public DQMEDAnalyzer { protected: void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; void analyze(const edm::Event&, const edm::EventSetup&) override; + std::string doubleToString(double x) const; edm::EDGetTokenT PFCandToken_; - edm::EDGetTokenT PFClusterHCALToken_; - edm::EDGetTokenT SimClusterHCALToken_; - edm::EDGetTokenT> RecoToSimAssociatorHCALToken_; - edm::EDGetTokenT> SimToRecoAssociatorHCALToken_; - edm::EDGetTokenT> RecoToCpAssociatorHCALToken_; - edm::EDGetTokenT> CpToRecoAssociatorHCALToken_; + edm::EDGetTokenT PFClusterToken_; + edm::EDGetTokenT SimClusterToken_; + edm::EDGetTokenT> RecoToSimAssociatorToken_; + edm::EDGetTokenT> SimToRecoAssociatorToken_; + edm::EDGetTokenT> RecoToCpAssociatorToken_; + edm::EDGetTokenT> CpToRecoAssociatorToken_; MonitorElement* h_PFCandEt_; MonitorElement* h_PFCandEta_; @@ -68,38 +68,67 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_PFClusterHitFraction_; MonitorElement* h_PFClusterHitDetId_; - double assocScoreThreshold_; + MonitorElement* h_simToRecoScore_; + MonitorElement* h_recoToSimScore_; - std::unordered_map> histoVars = { - {"Energy", std::make_tuple(100, 0., 50.)}, - {"Pt", std::make_tuple(100, 0., 40.)}, + std::vector assocScoreThresholds_; + + const std::unordered_map> histoVars = { + {"En", std::make_tuple(100, 0., 50.)}, + {"Pt", std::make_tuple(200, 0., 100.)}, + {"PtLow", std::make_tuple(100, 0., 10.)}, {"Eta", std::make_tuple(50, -6.5, 6.5)}, {"Phi", std::make_tuple(50, -3.5, 3.5)}, - {"Mult", std::make_tuple(20, 0., 20.)}, + {"Mult", std::make_tuple(200, 0., 200.)}, }; using UMap = std::unordered_map; + using VUMap = std::vector; UMap h_simClusters_; - UMap h_simClustersMatchedRecoClusters_; - UMap h_simClustersMultiMatchedRecoClusters_; + UMap h_simClustersReconstructable_; + VUMap h_simClustersMatchedRecoClusters_{histoVars.size()}; + VUMap h_simClustersMultiMatchedRecoClusters_{histoVars.size()}; UMap h_recoClusters_; - UMap h_recoClustersMatchedSimClusters_; - UMap h_recoClustersMultiMatchedSimClusters_; + UMap h_recoClustersReconstructable_; + VUMap h_recoClustersMatchedSimClusters_{histoVars.size()}; + VUMap h_recoClustersMultiMatchedSimClusters_{histoVars.size()}; + + std::unordered_map> histo2dVars = { + {"En_Eta", std::make_tuple(100, 0., 50., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 50., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 50., 200, 0., 200.)}, + {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, + {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, + {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, + {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, + {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + }; + + using U2Map = std::unordered_map; + using VU2Map = std::vector>; + U2Map h2d_simClusters_; + U2Map h2d_simClustersReconstructable_; + VU2Map h2d_simClustersMatchedRecoClusters_{histo2dVars.size()}; + U2Map h2d_recoClusters_; + U2Map h2d_recoClustersReconstructable_; + VU2Map h2d_recoClustersMatchedSimClusters_{histo2dVars.size()}; + + VU2Map h2d_response_{histoVars.size()}; }; PFTester::PFTester(const edm::ParameterSet& iConfig) : PFCandToken_(consumes(iConfig.getParameter("PFCand"))), - PFClusterHCALToken_(consumes(iConfig.getParameter("PFClusterHCAL"))), - SimClusterHCALToken_(consumes(iConfig.getParameter("SimClusterHCAL"))), - RecoToSimAssociatorHCALToken_(consumes>( - iConfig.getParameter("PFClusterSimClusterAssociatorHCAL"))), - SimToRecoAssociatorHCALToken_(consumes>( - iConfig.getParameter("PFClusterSimClusterAssociatorHCAL"))), - RecoToCpAssociatorHCALToken_(consumes>( - iConfig.getParameter("PFClusterCaloParticleAssociatorHCAL"))), - CpToRecoAssociatorHCALToken_(consumes>( - iConfig.getParameter("PFClusterCaloParticleAssociatorHCAL"))), - assocScoreThreshold_(iConfig.getParameter("assocScoreThreshold")) {} + PFClusterToken_(consumes(iConfig.getParameter("PFCluster"))), + SimClusterToken_(consumes(iConfig.getParameter("SimCluster"))), + RecoToSimAssociatorToken_(consumes>( + iConfig.getParameter("PFClusterSimClusterAssociator"))), + SimToRecoAssociatorToken_(consumes>( + iConfig.getParameter("PFClusterSimClusterAssociator"))), + RecoToCpAssociatorToken_(consumes>( + iConfig.getParameter("PFClusterCaloParticleAssociator"))), + CpToRecoAssociatorToken_(consumes>( + iConfig.getParameter("PFClusterCaloParticleAssociator"))), + assocScoreThresholds_(iConfig.getParameter>("assocScoreThresholds")) {} void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { ibook.setCurrentFolder("HLT/ParticleFlow/PFCandidates"); @@ -138,37 +167,128 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_PFClusterHitDetId_ = ibook.book1D("PFClusterHitDetId", "PFCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation"); + h_simToRecoScore_ = ibook.book1D("simToRecoScore", "simToRecoScore;Sim #rightarrow Reco score", 50, 0, 1); + h_recoToSimScore_ = ibook.book1D("recoToSimScore", "recoToSimScore;Reco #rightarrow Sim score", 50, 0, 1); + for (auto& hVar : histoVars) { auto [nBins, hMin, hMax] = hVar.second; + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation"); h_simClusters_[hVar.first] = ibook.book1D("SimClusters" + hVar.first, "SimClusters;" + hVar.first, nBins, hMin, hMax); - h_simClustersMatchedRecoClusters_[hVar.first] = ibook.book1D("SimClustersMatchedRecoClusters" + hVar.first, - "SimClusters matched to RecoClusters;" + hVar.first, - nBins, - hMin, - hMax); - h_simClustersMultiMatchedRecoClusters_[hVar.first] = - ibook.book1D("SimClustersMultiMatchedRecoClusters" + hVar.first, - "SimClusters multi-matched to RecoClusters;" + hVar.first, - nBins, - hMin, - hMax); - + h_simClustersReconstructable_[hVar.first] = ibook.book1D( + "SimClustersReconstructable" + hVar.first, "SimClustersReconstructable;" + hVar.first, nBins, hMin, hMax); h_recoClusters_[hVar.first] = ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); + h_recoClustersReconstructable_[hVar.first] = ibook.book1D( + "RecoClustersReconstructable" + hVar.first, "RecoClustersReconstructable;" + hVar.first, nBins, hMin, hMax); + + for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation/" + threshStr); + h_simClustersMatchedRecoClusters_[ithr][hVar.first] = + ibook.book1D("SimClustersMatchedRecoClusters" + hVar.first, + "SimClusters matched to RecoClusters;" + hVar.first, + nBins, + hMin, + hMax); + h_simClustersMultiMatchedRecoClusters_[ithr][hVar.first] = + ibook.book1D("SimClustersMultiMatchedRecoClusters" + hVar.first, + "SimClusters multi-matched to RecoClusters;" + hVar.first, + nBins, + hMin, + hMax); + + h_recoClustersMatchedSimClusters_[ithr][hVar.first] = + ibook.book1D("RecoClustersMatchedSimClusters" + hVar.first, + "RecoClusters matched to SimClusters;" + hVar.first, + nBins, + hMin, + hMax); + h_recoClustersMultiMatchedSimClusters_[ithr][hVar.first] = + ibook.book1D("RecoClustersMultiMatchedSimClusters" + hVar.first, + "RecoClusters multi-matched to SimClusters;" + hVar.first, + nBins, + hMin, + hMax); + } + } - h_recoClustersMatchedSimClusters_[hVar.first] = ibook.book1D("RecoClustersMatchedSimClusters" + hVar.first, - "RecoClusters matched to SimClusters;" + hVar.first, - nBins, - hMin, - hMax); - h_recoClustersMultiMatchedSimClusters_[hVar.first] = - ibook.book1D("RecoClustersMultiMatchedSimClusters" + hVar.first, - "RecoClusters multi-matched to SimClusters;" + hVar.first, - nBins, - hMin, - hMax); + for (auto& h2dVar : histo2dVars) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation"); + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); + h2d_simClusters_[h2dVar.first] = ibook.book2D("SimClusters" + h2dVar.first, + "SimClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + h2d_simClustersReconstructable_[h2dVar.first] = + ibook.book2D("SimClustersReconstructable" + h2dVar.first, + "SimClustersReconstructable;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + h2d_recoClusters_[h2dVar.first] = ibook.book2D("RecoClusters" + h2dVar.first, + "RecoClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + h2d_recoClustersReconstructable_[h2dVar.first] = + ibook.book2D("RecoClustersReconstructable" + h2dVar.first, + "RecoClustersReconstructable;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + + for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation/" + threshStr); + h2d_simClustersMatchedRecoClusters_[ithr][h2dVar.first] = + ibook.book2D("SimClustersMatchedRecoClusters" + h2dVar.first, + "SimClusters matched to RecoClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + h2d_recoClustersMatchedSimClusters_[ithr][h2dVar.first] = + ibook.book2D("RecoClustersMatchedSimClusters" + h2dVar.first, + "RecoClusters matched to SimClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + } + } + + for (auto& hVar : histoVars) { + auto [nBins, hMin, hMax] = hVar.second; + for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation/" + threshStr); + h2d_response_[ithr][hVar.first] = + ibook.book2D("Response" + hVar.first, "Response;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); + } } } @@ -192,53 +312,72 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { } // -------------------------------------------------------------------- - // ---------------- PF Cluster Efficiency ----------------------------- + // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- - edm::Handle PFClusterHCAL; - iEvent.getByToken(PFClusterHCALToken_, PFClusterHCAL); - if (!PFClusterHCAL.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterHCAL collection not found."; + edm::Handle PFCluster; + iEvent.getByToken(PFClusterToken_, PFCluster); + if (!PFCluster.isValid()) { + edm::LogInfo("PFTester") << "Input PFCluster collection not found."; return; } - auto recoClusters = *PFClusterHCAL; + auto recoClusters = *PFCluster; - edm::Handle SimClusterHCAL; - iEvent.getByToken(SimClusterHCALToken_, SimClusterHCAL); - if (!SimClusterHCAL.isValid()) { - edm::LogInfo("PFTester") << "Input SimClusterHCAL collection not found."; + edm::Handle SimCluster; + iEvent.getByToken(SimClusterToken_, SimCluster); + if (!SimCluster.isValid()) { + edm::LogInfo("PFTester") << "Input SimCluster collection not found."; return; } - auto simClusters = *SimClusterHCAL; + auto simClusters = *SimCluster; - edm::Handle> SimToRecoAssociatorHCALCollection; - iEvent.getByToken(SimToRecoAssociatorHCALToken_, SimToRecoAssociatorHCALCollection); - if (!SimToRecoAssociatorHCALCollection.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociatorHCAL SimToReco collection not found."; + edm::Handle> SimToRecoAssociatorCollection; + iEvent.getByToken(SimToRecoAssociatorToken_, SimToRecoAssociatorCollection); + if (!SimToRecoAssociatorCollection.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociator SimToReco collection not found."; return; } - auto simToRecoAssoc = *SimToRecoAssociatorHCALCollection; + auto simToRecoAssoc = *SimToRecoAssociatorCollection; // std::cout << "simRecColl size : " << simToRecoAssoc.size() << std::endl; - edm::Handle> RecoToSimAssociatorHCALCollection; - iEvent.getByToken(RecoToSimAssociatorHCALToken_, RecoToSimAssociatorHCALCollection); - if (!RecoToSimAssociatorHCALCollection.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociatorHCAL RecoToSim collection not found."; + edm::Handle> RecoToSimAssociatorCollection; + iEvent.getByToken(RecoToSimAssociatorToken_, RecoToSimAssociatorCollection); + if (!RecoToSimAssociatorCollection.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociator RecoToSim collection not found."; return; } - auto recoToSimAssoc = *RecoToSimAssociatorHCALCollection; + auto recoToSimAssoc = *RecoToSimAssociatorCollection; + // -------------------------------------------------------------------- + // ----- Efficiency and merge computation at cluster level ------------ + // -------------------------------------------------------------------- std::vector recoIdsMerged; - // efficiency and merge rate computation for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - h_simClusters_["Energy"]->Fill(simClusters[simId].energy()); + // filter all sim clusters produced by a sim track which crossed the + // tracker/calorimeter boundary outside the barrel + auto const scTrack = simClusters[simId].g4Tracks()[0]; + const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel + continue; + + h_simClusters_["En"]->Fill(simClusters[simId].energy()); h_simClusters_["Pt"]->Fill(simClusters[simId].pt()); + h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); h_simClusters_["Eta"]->Fill(simClusters[simId].eta()); h_simClusters_["Phi"]->Fill(simClusters[simId].phi()); - h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfSimHits()); - - const edm::Ref simClusterRef(SimClusterHCAL, simId); + h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + + h2d_simClusters_["En_Eta"]->Fill(simClusters[simId].energy(), simClusters[simId].eta()); + h2d_simClusters_["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); + h2d_simClusters_["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); + h2d_simClusters_["Pt_Eta"]->Fill(simClusters[simId].pt(), simClusters[simId].eta()); + h2d_simClusters_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); + h2d_simClusters_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); + h2d_simClusters_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].eta()); + h2d_simClusters_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); + + const edm::Ref simClusterRef(SimCluster, simId); const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); if (simToRecoIt == simToRecoAssoc.end()) continue; @@ -246,70 +385,122 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (simToRecoMatched.empty()) continue; - bool wasNotFilled = true; + h_simClustersReconstructable_["En"]->Fill(simClusters[simId].energy()); + h_simClustersReconstructable_["Pt"]->Fill(simClusters[simId].pt()); + h_simClustersReconstructable_["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersReconstructable_["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersReconstructable_["Phi"]->Fill(simClusters[simId].phi()); + h_simClustersReconstructable_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + + h2d_simClustersReconstructable_["En_Eta"]->Fill(simClusters[simId].energy(), simClusters[simId].eta()); + h2d_simClustersReconstructable_["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); + h2d_simClustersReconstructable_["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); + h2d_simClustersReconstructable_["Pt_Eta"]->Fill(simClusters[simId].pt(), simClusters[simId].eta()); + h2d_simClustersReconstructable_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); + h2d_simClustersReconstructable_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); + h2d_simClustersReconstructable_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].eta()); + h2d_simClustersReconstructable_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); + + std::vector wasNotFilled(assocScoreThresholds_.size(), true); for (const auto& recoPair : simToRecoMatched) { const auto recoPairIdx = recoPair.first.index(); - if (recoPair.second.second < assocScoreThreshold_) - continue; + for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + const double& thresh = assocScoreThresholds_[ithr]; - // numerator histograms must be filled only once per sim cluster - if (wasNotFilled) { - wasNotFilled = false; - h_simClustersMatchedRecoClusters_["Energy"]->Fill(simClusters[simId].energy()); - h_simClustersMatchedRecoClusters_["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMatchedRecoClusters_["Eta"]->Fill(simClusters[simId].eta()); - h_simClustersMatchedRecoClusters_["Phi"]->Fill(simClusters[simId].phi()); - h_simClustersMatchedRecoClusters_["Mult"]->Fill(simClusters[simId].numberOfSimHits()); - } + h_simToRecoScore_->Fill(recoPair.second.second); + if (recoPair.second.second > thresh) + continue; - // discard reco clusters from merge counting if already considered for a previous sim cluster - const auto& mergeIt = std::find(recoIdsMerged.begin(), recoIdsMerged.end(), recoPairIdx); - if (mergeIt != recoIdsMerged.end()) - continue; - recoIdsMerged.push_back(recoPairIdx); - - const edm::Ref recoClusterRef(PFClusterHCAL, recoPairIdx); - const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); - assert(recoToSimIt != recoToSimAssoc.end()); - const auto& recoToSimMatched = recoToSimIt->val; - assert(!recoToSimMatched.empty()); - - // find how many reco clusters are associated to the matched sim cluster - unsigned nSimMerged = 0; - for (const auto& simPair : recoToSimMatched) { - if (simPair.second < assocScoreThreshold_) + // numerator histograms must be filled only once per sim cluster + // they are filled inside the recoPair loop to enable a different denominator per threshold + if (wasNotFilled[ithr]) { + wasNotFilled[ithr] = false; + h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); + h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); + h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + + h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(simClusters[simId].energy(), + simClusters[simId].eta()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), + simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), + simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simClusters[simId].eta()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), + simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), + simClusters[simId].eta()); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), + simClusters[simId].phi()); + } + + // discard reco clusters from merge counting if already considered for a previous sim cluster + const auto& mergeIt = std::find(recoIdsMerged.begin(), recoIdsMerged.end(), recoPairIdx); + if (mergeIt != recoIdsMerged.end()) continue; - ++nSimMerged; - } + recoIdsMerged.push_back(recoPairIdx); + + const edm::Ref recoClusterRef(PFCluster, recoPairIdx); + const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); + assert(recoToSimIt != recoToSimAssoc.end()); + const auto& recoToSimMatched = recoToSimIt->val; + assert(!recoToSimMatched.empty()); + + // find how many sim clusters are associated to the matched reco cluster + unsigned nSimMerged = 0; + for (const auto& simPair : recoToSimMatched) { + if (simPair.second > thresh) + continue; + ++nSimMerged; + } - if (nSimMerged > 1) { - h_simClustersMultiMatchedRecoClusters_["Energy"]->Fill(recoClusters[simId].energy()); - h_simClustersMultiMatchedRecoClusters_["Pt"]->Fill(recoClusters[simId].pt()); - h_simClustersMultiMatchedRecoClusters_["Eta"]->Fill(recoClusters[simId].eta()); - h_simClustersMultiMatchedRecoClusters_["Phi"]->Fill(recoClusters[simId].phi()); - h_simClustersMultiMatchedRecoClusters_["Mult"]->Fill(recoClusters[simId].size()); - } + if (nSimMerged > 1) { + h_simClustersMultiMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); + h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + } - // for (const auto& recoPair : simToRecoMatched) { - // std::cout << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() - // << " shared energy = " << recoPair.second.first - // << " score = " << recoPair.second.second << std::endl; - // } + // for (const auto& recoPair : simToRecoMatched) { + // std::cout << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() + // << " shared energy = " << recoPair.second.first + // << " score = " << recoPair.second.second << std::endl; + // } + } } } std::vector simIdsDuplicates; - // fake rate and duplicate computation + // -------------------------------------------------------------------- + // ----- Fakes and duplicates computation at cluster level ------------ + // -------------------------------------------------------------------- + for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { - h_recoClusters_["Energy"]->Fill(recoClusters[recoId].energy()); + h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); - const edm::Ref recoClusterRef(PFClusterHCAL, recoId); + h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); + h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); + h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); + h2d_recoClusters_["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); + h2d_recoClusters_["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); + h2d_recoClusters_["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); + h2d_recoClusters_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); + h2d_recoClusters_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); + + const edm::Ref recoClusterRef(PFCluster, recoId); const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); if (recoToSimIt == recoToSimAssoc.end()) continue; @@ -317,51 +508,172 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (recoToSimMatched.empty()) continue; - bool wasNotFilled = true; + h_recoClustersReconstructable_["En"]->Fill(recoClusters[recoId].energy()); + h_recoClustersReconstructable_["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersReconstructable_["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClustersReconstructable_["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClustersReconstructable_["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClustersReconstructable_["Mult"]->Fill(recoClusters[recoId].size()); + + h2d_recoClustersReconstructable_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); + h2d_recoClustersReconstructable_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); + h2d_recoClustersReconstructable_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); + h2d_recoClustersReconstructable_["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); + h2d_recoClustersReconstructable_["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); + h2d_recoClustersReconstructable_["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); + h2d_recoClustersReconstructable_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); + h2d_recoClustersReconstructable_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); + + std::vector wasNotFilled(assocScoreThresholds_.size(), true); for (const auto& simPair : recoToSimMatched) { const auto simPairIdx = simPair.first.index(); // std::cout << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx // << " score = " << simPair.second << std::endl; - if (simPair.second < assocScoreThreshold_) + // filter all sim clusters produced by a sim track which crossed the + // tracker/calorimeter boundary outside the barrel + auto const scTrack = simClusters[simPairIdx].g4Tracks()[0]; + const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel continue; - // numerator histograms must be filled only once per reco cluster - if (wasNotFilled) { - wasNotFilled = false; - h_recoClustersMatchedSimClusters_["Energy"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMatchedSimClusters_["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMatchedSimClusters_["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClustersMatchedSimClusters_["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClustersMatchedSimClusters_["Mult"]->Fill(recoClusters[recoId].size()); + for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + const double& thresh = assocScoreThresholds_[ithr]; + + h_recoToSimScore_->Fill(simPair.second); + if (simPair.second > thresh) + continue; + + // numerator histograms must be filled only once per reco cluster + if (wasNotFilled[ithr]) { + wasNotFilled[ithr] = false; + h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); + h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); + + h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Eta"]->Fill(recoClusters[recoId].pt(), + recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Phi"]->Fill(recoClusters[recoId].pt(), + recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Mult"]->Fill(recoClusters[recoId].pt(), + recoClusters[recoId].size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), + recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), + recoClusters[recoId].phi()); + } + + // discard sim clusters from duplicate counting if already considered for a previous reco cluster + const auto& dupIt = std::find(simIdsDuplicates.begin(), simIdsDuplicates.end(), simPairIdx); + if (dupIt != simIdsDuplicates.end()) + continue; + simIdsDuplicates.push_back(simPairIdx); + + const edm::Ref simClusterRef(SimCluster, simPairIdx); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + assert(simToRecoIt != simToRecoAssoc.end()); + const auto& simToRecoMatched = simToRecoIt->val; + assert(!simToRecoMatched.empty()); + + // find how many reco clusters are associated to the matched sim cluster + unsigned nRecoDuplicates = 0; + for (const auto& recoPair : simToRecoMatched) { + if (recoPair.second.second > thresh) + continue; + ++nRecoDuplicates; + } + + if (nRecoDuplicates > 1) { + h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMultiMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); + } } + } + } - // discard sim clusters from duplicate counting if already considered for a previous reco cluster - const auto& dupIt = std::find(simIdsDuplicates.begin(), simIdsDuplicates.end(), simPairIdx); - if (dupIt != simIdsDuplicates.end()) - continue; - simIdsDuplicates.push_back(simPairIdx); + // -------------------------------------------------------------------- + // ----- Cluster response computation --------------------------------- + // -------------------------------------------------------------------- + + for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + // filter all sim clusters produced by a sim track which crossed the + // tracker/calorimeter boundary outside the barrel + auto const scTrack = simClusters[simId].g4Tracks()[0]; + const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel + continue; + + const edm::Ref simClusterRef(SimCluster, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + + for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + const double& thresh = assocScoreThresholds_[ithr]; - const edm::Ref simClusterRef(SimClusterHCAL, simPairIdx); - const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); - assert(simToRecoIt != simToRecoAssoc.end()); - const auto& simToRecoMatched = simToRecoIt->val; - assert(!simToRecoMatched.empty()); + bool fill = true; - // find how many reco clusters are associated to the matched sim cluster + // check for duplicate reco clusters + unsigned recoId = 0; unsigned nRecoDuplicates = 0; for (const auto& recoPair : simToRecoMatched) { - if (recoPair.second.second < assocScoreThreshold_) + if (recoPair.second.second > thresh) continue; ++nRecoDuplicates; + recoId = recoPair.first.index(); // used for the response computation + } + // only one reco cluster should be associated for the response + if (nRecoDuplicates != 1) { + fill = false; + } else { // check for merged sim cluster + + const edm::Ref recoClusterRef(PFCluster, recoId); + const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); + assert(recoToSimIt != recoToSimAssoc.end()); + const auto& recoToSimMatched = recoToSimIt->val; + assert(!recoToSimMatched.empty()); + + // find how many sim clusters are associated to the matched reco cluster + unsigned nSimMerged = 0; + for (const auto& simPair : recoToSimMatched) { + if (simPair.second > thresh) + continue; + ++nSimMerged; + } + + // only one sim cluster should be associated for the response + if (nSimMerged != 1) { + fill = false; + } } - if (nRecoDuplicates > 1) { - h_recoClustersMultiMatchedSimClusters_["Energy"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMultiMatchedSimClusters_["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMultiMatchedSimClusters_["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClustersMultiMatchedSimClusters_["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClustersMultiMatchedSimClusters_["Mult"]->Fill(recoClusters[recoId].size()); + if (fill) { + h2d_response_[ithr]["En"]->Fill(simClusters[simId].energy(), + recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_response_[ithr]["Pt"]->Fill(simClusters[simId].pt(), + recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_response_[ithr]["Eta"]->Fill(simClusters[simId].eta(), + recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_response_[ithr]["Phi"]->Fill(simClusters[simId].phi(), + recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_response_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), + recoClusters[recoId].pt() / simClusters[simId].pt()); } } } @@ -369,17 +681,17 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { // -------------------------------------------------------------------- // -------------------------------------------------------------------- - edm::Handle> RecoToCpAssociatorHCALCollection; - iEvent.getByToken(RecoToCpAssociatorHCALToken_, RecoToCpAssociatorHCALCollection); - if (!RecoToCpAssociatorHCALCollection.isValid()) { - std::cout << "Input PFClusterCpClusterAssociatorHCAL RecoToSim collection not found." << std::endl; - edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociatorHCAL RecoToSim collection not found."; + edm::Handle> RecoToCpAssociatorCollection; + iEvent.getByToken(RecoToCpAssociatorToken_, RecoToCpAssociatorCollection); + if (!RecoToCpAssociatorCollection.isValid()) { + std::cout << "Input PFClusterCpClusterAssociator RecoToSim collection not found." << std::endl; + edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociator RecoToSim collection not found."; } else { - auto recCpColl = *RecoToCpAssociatorHCALCollection; + auto recCpColl = *RecoToCpAssociatorCollection; // std::cout << "recCpColl size : " << recCpColl.size() << std::endl; for (unsigned int cId = 0; cId < recoClusters.size(); ++cId) { - const edm::Ref clusterRef(PFClusterHCAL, cId); + const edm::Ref clusterRef(PFCluster, cId); const auto& scsIt = recCpColl.find(clusterRef); if (scsIt == recCpColl.end()) continue; @@ -393,13 +705,13 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { } } - edm::Handle> CpToRecoAssociatorHCALCollection; - iEvent.getByToken(CpToRecoAssociatorHCALToken_, CpToRecoAssociatorHCALCollection); - if (!CpToRecoAssociatorHCALCollection.isValid()) { - std::cout << "Input PFClusterCpClusterAssociatorHCAL SimToReco collection not found." << std::endl; - edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociatorHCAL SimToReco collection not found."; + edm::Handle> CpToRecoAssociatorCollection; + iEvent.getByToken(CpToRecoAssociatorToken_, CpToRecoAssociatorCollection); + if (!CpToRecoAssociatorCollection.isValid()) { + std::cout << "Input PFClusterCpClusterAssociator SimToReco collection not found." << std::endl; + edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociator SimToReco collection not found."; } else { - auto CpRecColl = *CpToRecoAssociatorHCALCollection; + auto CpRecColl = *CpToRecoAssociatorCollection; // std::cout << "CpRecColl size : " << CpRecColl.size() << std::endl; } @@ -504,4 +816,19 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { } } +// convert a double to the string format +std::string PFTester::doubleToString(double x) const { + std::ostringstream result; + result << std::setprecision(2) << x; + + std::string xnew = result.str(); + std::size_t pos = xnew.find("."); + if (pos != std::string::npos) + xnew.replace(pos, 1, "p"); + else //if the double was provided without decimal places + xnew += "p0"; + + return xnew; +} + DEFINE_FWK_MODULE(PFTester); diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 6086545bff864..0115a5d8786fc 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -1,54 +1,81 @@ import FWCore.ParameterSet.Config as cms from DQMServices.Core.DQMEDHarvester import DQMEDHarvester +from Validation.RecoParticleFlow.hltPFValidation_cfi import hltPFTesterECAL +_thresholds = [str(x).replace('.', 'p') for x in hltPFTesterECAL.assocScoreThresholds] + hltPFClusterPostProcessor = DQMEDHarvester( "DQMGenericClient", subDirs=cms.untracked.vstring("HLT/ParticleFlow/PFClusterValidation"), efficiency = cms.vstring( - "Eff_vs_EnergyEta 'Efficiency vs Energy-#eta' SimClustersMatchedRecoClustersEnergy_Eta SimClustersEnergy_Eta", - "Eff_vs_EnergyPhi 'Efficiency vs Energy-#phi' SimClustersMatchedRecoClustersEnergy_Phi SimClustersEnergy_Phi", - "Eff_vs_EnergyMult 'Efficiency vs Energy-Mult' SimClustersMatchedRecoClustersEnergy_Mult SimClustersEnergy_Mult", - "Eff_vs_PtEta 'Efficiency vs p_{T}-#eta' SimClustersMatchedRecoClustersPt_Eta SimClustersPt_Eta", - "Eff_vs_PtPhi 'Efficiency vs p_{T}-#phi' SimClustersMatchedRecoClustersPt_Phi SimClustersPt_Phi", - "Eff_vs_PtMult 'Efficiency vs p_{T}-Mult' SimClustersMatchedRecoClustersPt_Mult SimClustersPt_Mult", - "Eff_vs_MultEta 'Efficiency vs Mult-#eta' SimClustersMatchedRecoClustersMult_Eta SimClustersMult_Eta", - "Eff_vs_MultPhi 'Efficiency vs Mult-#phi' SimClustersMatchedRecoClustersMult_Phi SimClustersMult_Phi", - "Fake_vs_EnergyEta 'Fake Rate vs Energy-#eta' RecoClustersMatchedSimClustersEnergy_Eta RecoClustersPt_Eta fake", - "Fake_vs_EnergyPhi 'Fake Rate vs Energy-#phi' RecoClustersMatchedSimClustersEnergy_Phi RecoClustersPt_Phi fake", - "Fake_vs_EnergyMult 'Fake Rate vs Energy-Mult' RecoClustersMatchedSimClustersEnergy_Mult RecoClustersPt_Mult fake", - "Fake_vs_PtEta 'Fake Rate vs p_{T}-#eta' RecoClustersMatchedSimClustersPt_Eta RecoClustersPt_Eta fake", - "Fake_vs_PtPhi 'Fake Rate vs p_{T}-#phi' RecoClustersMatchedSimClustersPt_Phi RecoClustersPt_Phi fake", - "Fake_vs_PtMult 'Fake Rate vs p_{T}-Mult' RecoClustersMatchedSimClustersPt_Mult RecoClustersPt_Mult fake", - "Fake_vs_MultEta 'Fake Rate vs Mult-#eta' RecoClustersMatchedSimClustersMult_Eta RecoClustersMult_Eta fake", - "Fake_vs_MultPhi 'Fake Rate vs Mult-#phi' RecoClustersMatchedSimClustersMult_Phi RecoClustersMult_Phi fake", + *[ item + for thr in _thresholds + for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) + for item in ( + f"'Score{thr}/Eff_vs_EnergyEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", + f"'Score{thr}/Eff_vs_EnergyPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", + f"'Score{thr}/Eff_vs_EnergyMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", + f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", + f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", + f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", + f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", + f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", + f"'Score{thr}/Fake_vs_EnergyEta{name}' 'Fake Rate vs Energy-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClusters{suf}Pt_Eta fake", + f"'Score{thr}/Fake_vs_EnergyPhi{name}' 'Fake Rate vs Energy-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClusters{suf}Pt_Phi fake", + f"'Score{thr}/Fake_vs_EnergyMult{name}' 'Fake Rate vs Energy-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClusters{suf}Pt_Mult fake", + f"'Score{thr}/Fake_vs_PtEta{name}' 'Fake Rate vs p_{{T}}-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClusters{suf}Pt_Eta fake", + f"'Score{thr}/Fake_vs_PtPhi{name}' 'Fake Rate vs p_{{T}}-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClusters{suf}Pt_Phi fake", + f"'Score{thr}/Fake_vs_PtMult{name}' 'Fake Rate vs p_{{T}}-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClusters{suf}Pt_Mult fake", + f"'Score{thr}/Fake_vs_MultEta{name}' 'Fake Rate vs Mult-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Eta RecoClusters{suf}Mult_Eta fake", + f"'Score{thr}/Fake_vs_MultPhi{name}' 'Fake Rate vs Mult-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClusters{suf}Mult_Phi fake", + ) + ], ), efficiencyProfile = cms.untracked.vstring( # for smoother rebinning - # Efficiency - "Eff_vs_Energy 'Efficiency vs Energy' SimClustersMatchedRecoClustersEnergy SimClustersEnergy ", - "Eff_vs_Pt 'Efficiency vs p_{T}' SimClustersMatchedRecoClustersPt SimClustersPt ", - "Eff_vs_Eta 'Efficiency vs #eta' SimClustersMatchedRecoClustersEta SimClustersEta ", - "Eff_vs_Phi 'Efficiency vs #phi' SimClustersMatchedRecoClustersPhi SimClustersPhi ", - "Eff_vs_Mult 'Efficiency vs Multiplicity' SimClustersMatchedRecoClustersMult SimClustersMult ", - # Fake rate - "Fake_vs_Energy 'Fake Rate vs Energy' RecoClustersMatchedSimClustersEnergy RecoClustersEnergy ", - "Fake_vs_Pt 'Fake Rate vs p_{T}' RecoClustersMatchedSimClustersPt RecoClustersPt ", - "Fake_vs_Eta 'Fake Rate vs #eta' RecoClustersMatchedSimClustersEta RecoClustersEta ", - "Fake_vs_Phi 'Fake Rate vs #phi' RecoClustersMatchedSimClustersPhi RecoClustersPhi ", - "Fake_vs_Mult 'Fake Rate vs Multiplicity' RecoClustersMatchedSimClustersMult RecoClustersMult ", - # Duplicate rate - "Dup_vs_Energy 'Dup Rate vs Energy' RecoClustersMultiMatchedSimClustersEnergy RecoClustersEnergy ", - "Dup_vs_Pt 'Dup Rate vs p_{T}' RecoClustersMultiMatchedSimClustersPt RecoClustersPt ", - "Dup_vs_Eta 'Dup Rate vs #eta' RecoClustersMultiMatchedSimClustersEta RecoClustersEta ", - "Dup_vs_Phi 'Dup Rate vs #phi' RecoClustersMultiMatchedSimClustersPhi RecoClustersPhi ", - "Dup_vs_Mult 'Dup Rate vs Mult' RecoClustersMultiMatchedSimClustersMult RecoClustersMult ", - # Merge rate - "Merge_vs_Energy 'Merge Rate vs Energy' SimClustersMultiMatchedRecoClustersEnergy SimClustersEnergy ", - "Merge_vs_Pt 'Merge Rate vs p_{T}' SimClustersMultiMatchedRecoClustersPt SimClustersPt ", - "Merge_vs_Eta 'Merge Rate vs #eta' SimClustersMultiMatchedRecoClustersEta SimClustersEta ", - "Merge_vs_Phi 'Merge Rate vs #phi' SimClustersMultiMatchedRecoClustersPhi SimClustersPhi ", - "Merge_vs_Mult 'Merge Rate vs Multiplicity' SimClustersMultiMatchedRecoClustersMult SimClustersMult ", + *[ item + for thr in _thresholds + for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) + for item in ( + # Efficiency + f"'Score{thr}/Eff_vs_Energy{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Eff_vs_Pt{name}' 'Efficiency vs p_{{T}} {suf}' Score{thr}/SimClustersMatchedRecoClustersPt SimClusters{suf}Pt", + f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", + f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", + f"'Score{thr}/Eff_vs_Mult{name}' 'Efficiency vs Multiplicity {suf}' Score{thr}/SimClustersMatchedRecoClustersMult SimClusters{suf}Mult", + # Fake rate + f"'Score{thr}/Fake_vs_En{name}' 'Fake Rate vs Energy {suf}' Score{thr}/RecoClustersMatchedSimClustersEn RecoClusters{suf}En", + f"'Score{thr}/Fake_vs_Pt{name}' 'Fake Rate vs p_{{T}} {suf}' Score{thr}/RecoClustersMatchedSimClustersPt RecoClusters{suf}Pt", + f"'Score{thr}/Fake_vs_Eta{name}' 'Fake Rate vs #eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEta RecoClusters{suf}Eta", + f"'Score{thr}/Fake_vs_Phi{name}' 'Fake Rate vs #phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPhi RecoClusters{suf}Phi", + f"'Score{thr}/Fake_vs_Mult{name}' 'Fake Rate vs Multiplicity {suf}' Score{thr}/RecoClustersMatchedSimClustersMult RecoClusters{suf}Mult", + # Duplicate rate + f"'Score{thr}/Dup_vs_En{name}' 'Dup Rate vs Energy {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersEn RecoClusters{suf}En", + f"'Score{thr}/Dup_vs_Pt{name}' 'Dup Rate vs p_{{T}} {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersPt RecoClusters{suf}Pt", + f"'Score{thr}/Dup_vs_Eta{name}' 'Dup Rate vs #eta {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersEta RecoClusters{suf}Eta", + f"'Score{thr}/Dup_vs_Phi{name}' 'Dup Rate vs #phi {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersPhi RecoClusters{suf}Phi", + f"'Score{thr}/Dup_vs_Mult{name}' 'Dup Rate vs Mult {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClusters{suf}Mult", + # Merge rate + f"'Score{thr}/Merge_vs_En{name}' 'Merge Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Merge_vs_Pt{name}' 'Merge Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", + f"'Score{thr}/Merge_vs_Eta{name}' 'Merge Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", + f"'Score{thr}/Merge_vs_Phi{name}' 'Merge Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", + f"'Score{thr}/Merge_vs_Mult{name}' 'Merge Rate vs Multiplicity {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClusters{suf}Mult", + ) + ], ), resolution = cms.vstring(), + resolutionProfile = cms.untracked.vstring( + *[ item + for thr in _thresholds + for item in ( + f"'Score{thr}/ResponseEn' 'Response vs Energy' Score{thr}/ResponseEn rms", + f"'Score{thr}/ResponsePt' 'Response vs p_{{T}}' Score{thr}/ResponsePt rms", + f"'Score{thr}/ResponseEta' 'Response vs #eta' Score{thr}/ResponseEta rms", + f"'Score{thr}/ResponsePhi' 'Response vs #phi' Score{thr}/ResponsePhi rms", + f"'Score{thr}/ResponseMult' 'Response vs Multiplicity' Score{thr}/ResponseMult rms" + ) + ], + ), verbose = cms.untracked.uint32(2), outputFileName = cms.untracked.string("") ) diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index e23acf5adda75..278bc0b077138 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -26,11 +26,11 @@ hltPFTesterECAL = cms.EDProducer("PFTester", PFCand = cms.InputTag("hltParticleFlowTmp"), - PFClusterHCAL = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - SimClusterHCAL = cms.InputTag("mix","MergedCaloTruth"), - PFClusterSimClusterAssociatorHCAL = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), - PFClusterCaloParticleAssociatorHCAL = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), - assocScoreThreshold = cms.double(0.) + PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + SimCluster = cms.InputTag("mix","MergedCaloTruth"), + PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), + PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.1) ) PFValSeq = cms.Sequence( diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py old mode 100644 new mode 100755 index b2f4483f5d614..11deab15173cc --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -4,6 +4,7 @@ import argparse import numpy as np import hist +import re import matplotlib.pyplot as plt from matplotlib.transforms import blended_transform_factory plt.style.use('tableau-colorblind10') @@ -29,16 +30,25 @@ def createDir(adir): def checkRootDir(afile, adir): if not afile.Get(adir): raise RuntimeError(f"Directory '{adir}' not found in {afile}") + +def GetRootSubDir(afile, adir): + subdirs = [] + d = afile.GetDirectory(adir) + for key in d.GetListOfKeys(): + obj = key.ReadObj() + if isinstance(obj, ROOT.TDirectory): + subdirs.append(obj.GetName()) + return subdirs def checkRootFile(afile, hname, rebin=None): hist_orig = afile.Get(hname) if not hist_orig: raise RuntimeError(f"WARNING: Histogram {hname} not found.") - hist = hist_orig.Clone(hname + "_clone") - hist.SetDirectory(0) # detach from file - if rebin is not None: + hist = hist_orig.Clone(hname + "_clone") + hist.SetDirectory(0) # detach from file + if isinstance(rebin, (int, float)): hist = hist.Rebin(int(rebin), hname + "_rebin") elif hasattr(rebin, '__iter__'): @@ -47,6 +57,9 @@ def checkRootFile(afile, hname, rebin=None): else: raise ValueError(f"Unknown type for rebin: {type(rebin)}") + else: + hist = hist_orig + return hist @@ -155,7 +168,73 @@ def save(self, name): plt.savefig(name + '.' + ext) plt.close() -def plotEffComp1D(afile, adir, vars1d, outdir, text, top_text=False): +def plotOverlay(subdirs, adir, name, props, outdir): + """ + Plots 1D distributions, overlaying plots with identical names in different 'subdirs'. + """ + colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', + '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + pattern = r"Score(\d+)p(\d+)" + replacement = lambda m: f"score = {m.group(1)}.{m.group(2)}" + + plotter = Plotter(args.sample_label, grid_color=None) + for sub in subdirs: + root_hist = checkRootFile(afile, f"{adir}/{sub}/{name}", rebin=None) + nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) + values, errors = histo_values_errors(root_hist) + errors /= 2 + + line = plotter.ax.stairs(values, bin_edges, linewidth=2, + baseline=None, color=next(colors_iter)) + + sublabel = re.sub(pattern, replacement, sub) + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, + color=line.get_edgecolor(), + fmt='s', label=sublabel, **errorbar_kwargs) + + # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') + plotter.ax.grid(color='grey', axis='x') + plt.tight_layout() + plotter.save( os.path.join(outdir, name) ) + +def plotOverlayRatio(subdirs, adir, num, den, props, outdir): + """ + Plots 1D distributions of numerator / denominator. + """ + colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', + '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + pattern = r"Score(\d+)p(\d+)" + replacement = lambda m: f"score = {m.group(1)}.{m.group(2)}" + + plotter = Plotter(args.sample_label, grid_color=None) + for sub in subdirs: + hist_num = checkRootFile(afile, f"{adir}/{sub}/{num}", rebin=None) + hist_den = checkRootFile(afile, f"{adir}/{sub}/{den}", rebin=None) + + nbins, bin_edges, bin_centers, bin_widths = define_bins(hist_num) + num_vals, num_errors = histo_values_errors(hist_num) + den_vals, den_errors = histo_values_errors(hist_den) + + ratio_vals = [s / m if m != 0 else np.nan for s, m in zip(num_vals, den_vals)] + ratio_errors = [np.sqrt((ds / m)**2 + ((s / m) * (dm / m))**2) / 2 if m != 0 else np.nan + for s, ds, m, dm in zip(num_vals, num_errors, den_vals, den_errors)] + + line = plotter.ax.stairs(ratio_vals, bin_edges, linewidth=2, + baseline=None, color=next(colors_iter)) + + sublabel = re.sub(pattern, replacement, sub) + plotter.ax.errorbar(bin_centers, ratio_vals, xerr=None, yerr=ratio_errors, + color=line.get_edgecolor(), + fmt='s', label=sublabel, **errorbar_kwargs) + + # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') + plotter.ax.grid(color='grey', axis='x') + plt.tight_layout() + plotter.save( os.path.join(outdir, props['name']) ) + +def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix=''): """ Plots 1D distributions. The `avars` variables is a dictionary whose values are (xlabel, ylabel, rebin). @@ -168,16 +247,20 @@ def plotEffComp1D(afile, adir, vars1d, outdir, text, top_text=False): valuesList, errorsList = [], [] colors_iter = iter(('black', 'blue')) for avar in vars1d: - name, (xlabel, _, rebin, logy, leglabel) = avar + name, (xlabel, ylabel, rebin, logy, doNormalize, leglabel) = avar + if doNormalize: + ylabel = '[a.u.]' - root_hist = checkRootFile(afile, f"{adir}/{name}", rebin=rebin) + root_hist = cached_histos[name] nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) values, errors = histo_values_errors(root_hist) errors /= 2 # normalization - errors /= sum(values) - values /= sum(values) + doNormalize = False + if 'Eff' not in name and doNormalize: + errors /= sum(values) + values /= sum(values) if 'Eff' in name: # ax2.set_yscale('log') @@ -199,7 +282,7 @@ def plotEffComp1D(afile, adir, vars1d, outdir, text, top_text=False): errorsList.append(errors) plotter.limits_with_margin(valuesList, errorsList, logY=logy) - plotter.labels(x=xlabel, y='[a.u.]', legend_title='') + plotter.labels(x=xlabel, y=ylabel, legend_title='') plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, verticalalignment='top', horizontalalignment='left') @@ -214,7 +297,7 @@ def plotEffComp1D(afile, adir, vars1d, outdir, text, top_text=False): plotter.ax.grid(color=eff_color, axis='x') plt.tight_layout() - plotter.save( os.path.join(outdir, name + 'Comp') ) + plotter.save( os.path.join(outdir, name) ) def plot1Dvars(afile, adir, avars, outdir, text, top_text=False): for var, (xlabel, ylabel, rebin, logy, _) in avars.items(): @@ -240,7 +323,7 @@ def plot1Dvars(afile, adir, avars, outdir, text, top_text=False): verticalalignment='top', horizontalalignment='right') plt.tight_layout() - plotter.save( os.path.join(outdir, var) ) + plotter.save( os.path.join(outdir, var, suffix) ) if __name__ == '__main__': @@ -285,38 +368,78 @@ def __call__(self, parser, namespace, values, option_string=None): nEventsLabel = '# Events' effLabel = 'Efficiency' - vars1D = { - # PF tester producer - **{x + 'ClustersEnergy': ('Energy [GeV]', nEventsLabel, None, True, x) for x in ('Sim', 'Reco')}, - 'Eff_vs_Energy': ('Energy [GeV]', effLabel, None, True, None), - **{x + 'ClustersPt': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, x) for x in ('Sim', 'Reco')}, - 'Eff_vs_Pt': (r'$p_{T}$ [GeV]', effLabel, None, False, None), - **{x + 'ClustersEta': (r'$\eta$', nEventsLabel, None, False, x) for x in ('Sim', 'Reco')}, - 'Eff_vs_Eta': (r'$\eta$', effLabel, None, True, None), - **{x + 'ClustersPhi': (r'$\phi$', nEventsLabel, None, False, x) for x in ('Sim', 'Reco')}, - 'Eff_vs_Phi': (r'$\phi$', effLabel, None, True, None), - **{x + 'ClustersMult': ('Multiplicity', nEventsLabel, None, True, x) for x in ('Sim', 'Reco')}, - 'Eff_vs_Mult': ('Multiplicity', effLabel, None, False, None), - } dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation" - if args.compare_files is not None: - for afile, alabel in zip(args.compare_files, args.compare_files_labels): - afile = ROOT.TFile.Open(afile) - checkRootDir(afile, dqm_dir) - plot1DFilesComparison(dqm_dir, vars1D, outdir=args.odir, text='', - files=args.compare_files, - files_labels=args.compare_files_labels) - - else: - afile = ROOT.TFile.Open(args.file) - - # Plot 1D PF variables - checkRootDir(afile, dqm_dir) - # plot1Dvars(afile, dqm_dir, vars1D, outdir=args.odir, text='') - - # Compare pairs of variables - it = iter(vars1D.items()) - for var in it: - avars = (var, next(it), next(it)) # reco, sim and efficiency for a given variable - plotEffComp1D(afile, dqm_dir, vars1d=avars, outdir=args.odir, text='') + afile = ROOT.TFile.Open(args.file) + checkRootDir(afile, dqm_dir) + + print("### INFO: Start caching histograms...") + subdirs = [] + cached_histos = {} + directory = afile.GetDirectory(dqm_dir) + for key in directory.GetListOfKeys(): + obj = key.ReadObj() + if isinstance(obj, ROOT.TDirectory): + subdirs.append(obj.GetName()) + subdir = afile.GetDirectory(f"{dqm_dir}/{obj.GetName()}") + for subkey in subdir.GetListOfKeys(): + name = subkey.GetName() + cached_histos[f"{obj.GetName()}/{name}"] = subkey.ReadObj() + else: + name = key.GetName() + cached_histos[name] = key.ReadObj() + print("...done.") + + # subdirs = GetRootSubDir(afile, dqm_dir) + for subdir in subdirs: + checkRootDir(afile, f"{dqm_dir}/{subdir}") + createDir(f'{args.odir}/{subdir}') + for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')): + vars1D = { + # PF tester producer + f'SimClusters{suf}En': ('Energy [GeV]', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersEn': ('Energy [GeV]', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Eff_vs_Energy{name}': ('Energy [GeV]', nEventsLabel, None, True, False, None), + f'SimClusters{suf}Pt': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersPt': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Eff_vs_Pt{name}': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, None), + f'SimClusters{suf}Eta': (r'$\eta$', nEventsLabel, None, False, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersEta': (r'$\eta$', nEventsLabel, None, False, False, 'Reco'), + f'{subdir}/Eff_vs_Eta{name}': (r'$\eta$', nEventsLabel, None, False, False, None), + f'SimClusters{suf}Phi': (r'$\phi$', nEventsLabel, None, False, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersPhi': (r'$\phi$', nEventsLabel, None, False, False, 'Reco'), + f'{subdir}/Eff_vs_Phi{name}': (r'$\phi$', nEventsLabel, None, False, False, None), + f'SimClusters{suf}Mult': ('Multiplicity', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersMult': ('Multiplicity', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Eff_vs_Mult{name}': ('Multiplicity', nEventsLabel, None, True, False, None), + } + + # Compare pairs of variables + it = iter(vars1D.items()) + for var in it: + avars = (var, next(it), next(it)) # reco, sim and efficiency for a given variable + plotEffComp1D(cached_histos, vars1d=avars, outdir=args.odir, text='', suffix=f'') + + titles = {'response': r"$$", 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", 'eff': 'Efficiency'} + + varsOverlay = { + "ResponsePt_Mean" : dict(ytitle=titles['response'], xtitle=r'$p_{T} [GeV]$', logy=False), + "ResponseEta_Mean" : dict(ytitle=titles['response'], xtitle=r'$\eta$', logy=False), + "ResponsePhi_Mean" : dict(ytitle=titles['response'], xtitle=r'$\phi$', logy=False), + "ResponseMult_Mean" : dict(ytitle=titles['response'], xtitle='Multiplicity', logy=False), + "Eff_vs_Pt_Reconstructable" : dict(ytitle=titles['eff'], xtitle='$p_{T} [GeV]$', logy=False), + "Eff_vs_Eta_Reconstructable" : dict(ytitle=titles['eff'], xtitle=r'$\eta$', logy=False), + "Eff_vs_Phi_Reconstructable" : dict(ytitle=titles['eff'], xtitle=r'$\phi$', logy=False), + "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], xtitle='Multiplicity', logy=False), + } + for name, props in varsOverlay.items(): + plotOverlay(subdirs, dqm_dir, name, props, outdir=args.odir) + + varsResponse = { + ("ResponsePt_Sigma", "ResponsePt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', logy=False), + ("ResponseEta_Sigma", "ResponseEta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', logy=False), + ("ResponsePhi_Sigma", "ResponsePhi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', logy=False), + ("ResponseMult_Sigma", "ResponseMult_Mean") : dict(name='ResolutionMult',ytitle=titles['resolution'], xtitle='Multiplicity', logy=False), + } + for (num, den), props in varsResponse.items(): + plotOverlayRatio(subdirs, dqm_dir, num, den, props, outdir=args.odir) From 8a781dbb1b35916a47b250ee6bc852d6af3bd19f Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 23 Oct 2025 11:37:21 +0200 Subject: [PATCH 03/64] Implement 2d histograms and slice projections. --- .../python/hltPFPostProcessor_cfi.py | 14 +- .../scripts/makeHLTPFValidationPlots.py | 270 ++++++++++++------ 2 files changed, 191 insertions(+), 93 deletions(-) diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 0115a5d8786fc..5c595e0daab28 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -12,17 +12,17 @@ for thr in _thresholds for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) for item in ( - f"'Score{thr}/Eff_vs_EnergyEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", - f"'Score{thr}/Eff_vs_EnergyPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", - f"'Score{thr}/Eff_vs_EnergyMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", + f"'Score{thr}/Eff_vs_EnEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", + f"'Score{thr}/Eff_vs_EnPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", + f"'Score{thr}/Eff_vs_EnMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", - f"'Score{thr}/Fake_vs_EnergyEta{name}' 'Fake Rate vs Energy-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClusters{suf}Pt_Eta fake", - f"'Score{thr}/Fake_vs_EnergyPhi{name}' 'Fake Rate vs Energy-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClusters{suf}Pt_Phi fake", - f"'Score{thr}/Fake_vs_EnergyMult{name}' 'Fake Rate vs Energy-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClusters{suf}Pt_Mult fake", + f"'Score{thr}/Fake_vs_EnEta{name}' 'Fake Rate vs Energy-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClusters{suf}Pt_Eta fake", + f"'Score{thr}/Fake_vs_EnPhi{name}' 'Fake Rate vs Energy-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClusters{suf}Pt_Phi fake", + f"'Score{thr}/Fake_vs_EnMult{name}' 'Fake Rate vs Energy-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClusters{suf}Pt_Mult fake", f"'Score{thr}/Fake_vs_PtEta{name}' 'Fake Rate vs p_{{T}}-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClusters{suf}Pt_Eta fake", f"'Score{thr}/Fake_vs_PtPhi{name}' 'Fake Rate vs p_{{T}}-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClusters{suf}Pt_Phi fake", f"'Score{thr}/Fake_vs_PtMult{name}' 'Fake Rate vs p_{{T}}-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClusters{suf}Pt_Mult fake", @@ -37,7 +37,7 @@ for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) for item in ( # Efficiency - f"'Score{thr}/Eff_vs_Energy{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Eff_vs_En{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", f"'Score{thr}/Eff_vs_Pt{name}' 'Efficiency vs p_{{T}} {suf}' Score{thr}/SimClustersMatchedRecoClustersPt SimClusters{suf}Pt", f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 11deab15173cc..71f10dd143735 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -27,41 +27,15 @@ def createDir(adir): os.makedirs(adir) return adir +def createIndexPHP(src, dest): + php_file = os.path.join(src, 'index.php') + if os.path.exists(php_file): + os.system(f'cp {php_file} {dest}') + return adir + def checkRootDir(afile, adir): if not afile.Get(adir): raise RuntimeError(f"Directory '{adir}' not found in {afile}") - -def GetRootSubDir(afile, adir): - subdirs = [] - d = afile.GetDirectory(adir) - for key in d.GetListOfKeys(): - obj = key.ReadObj() - if isinstance(obj, ROOT.TDirectory): - subdirs.append(obj.GetName()) - return subdirs - -def checkRootFile(afile, hname, rebin=None): - hist_orig = afile.Get(hname) - if not hist_orig: - raise RuntimeError(f"WARNING: Histogram {hname} not found.") - - if rebin is not None: - hist = hist_orig.Clone(hname + "_clone") - hist.SetDirectory(0) # detach from file - - if isinstance(rebin, (int, float)): - hist = hist.Rebin(int(rebin), hname + "_rebin") - elif hasattr(rebin, '__iter__'): - bin_edges_c = array.array('d', rebin) - hist = hist.Rebin(len(bin_edges_c) - 1, hname + "_rebin", bin_edges_c) - else: - raise ValueError(f"Unknown type for rebin: {type(rebin)}") - - else: - hist = hist_orig - - return hist - def rate_errorbar_declutter(eff, err, yaxmin, frac=0.01): """ @@ -93,13 +67,34 @@ def define_bins(h): edges = np.array([h.GetBinLowEdge(i+1) for i in range(N)]) edges = np.append(edges, h.GetBinLowEdge(N+1)) return N, edges, 0.5*(edges[:-1]+edges[1:]), np.diff(edges) - + +def define_bins_2D(h): + Nx = h.GetNbinsX() + Ny = h.GetNbinsY() + + x_edges = np.array([h.GetXaxis().GetBinLowEdge(i+1) for i in range(Nx)]) + x_edges = np.append(x_edges, h.GetXaxis().GetBinUpEdge(Nx)) + + y_edges = np.array([h.GetYaxis().GetBinLowEdge(j+1) for j in range(Ny)]) + y_edges = np.append(y_edges, h.GetYaxis().GetBinUpEdge(Ny)) + + return Nx, Ny, x_edges, y_edges + def histo_values_errors(h): N = h.GetNbinsX() values = np.array([h.GetBinContent(i+1) for i in range(N)]) errors = np.array([h.GetBinError(i+1) for i in range(N)]) return values, errors +def histo_values_2D(h, error=False): + Nx = h.GetNbinsX() + Ny = h.GetNbinsY() + values = np.array([ + [h.GetBinContent(i+1, j+1) for i in range(Nx)] + for j in range(Ny) + ]) + return values + class Plotter: def __init__(self, label, fontsize=18, grid_color='grey'): self._fig, self._ax = plt.subplots(figsize=(10, 10)) @@ -168,7 +163,36 @@ def save(self, name): plt.savefig(name + '.' + ext) plt.close() -def plotOverlay(subdirs, adir, name, props, outdir): +def plotProject(h, props, rebin_edges, outname): + """ + Project and plot slices of a 2D histogram. + """ + colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', + '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + + plotter = Plotter(args.sample_label, grid_color=None) + for ibin, (low, high) in enumerate(zip(rebin_edges[:-1],rebin_edges[1:])): + hproj = h.ProjectionY(h.GetName() + "_proj" + str(ibin), + h.GetXaxis().FindBin(low), h.GetXaxis().FindBin(high), "e") + + nbins, bin_edges, bin_centers, bin_widths = define_bins(hproj) + values, errors = histo_values_errors(hproj) + errors /= 2 + + line = plotter.ax.stairs(values, bin_edges, linewidth=2, + baseline=None, color=next(colors_iter)) + label = f"{low} < {props['var']} < {high} {props['unit']}" + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, + color=line.get_edgecolor(), + fmt='s', label=label, **errorbar_kwargs) + + # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') + plotter.ax.grid(color='grey', axis='x') + plt.tight_layout() + plotter.save(outname) + +def plotOverlay(subdirs, cached_histos, name, props, outdir): """ Plots 1D distributions, overlaying plots with identical names in different 'subdirs'. """ @@ -179,7 +203,7 @@ def plotOverlay(subdirs, adir, name, props, outdir): plotter = Plotter(args.sample_label, grid_color=None) for sub in subdirs: - root_hist = checkRootFile(afile, f"{adir}/{sub}/{name}", rebin=None) + root_hist = cached_histos[f"{sub}/{name}"] nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) values, errors = histo_values_errors(root_hist) errors /= 2 @@ -198,7 +222,7 @@ def plotOverlay(subdirs, adir, name, props, outdir): plt.tight_layout() plotter.save( os.path.join(outdir, name) ) -def plotOverlayRatio(subdirs, adir, num, den, props, outdir): +def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): """ Plots 1D distributions of numerator / denominator. """ @@ -209,8 +233,24 @@ def plotOverlayRatio(subdirs, adir, num, den, props, outdir): plotter = Plotter(args.sample_label, grid_color=None) for sub in subdirs: - hist_num = checkRootFile(afile, f"{adir}/{sub}/{num}", rebin=None) - hist_den = checkRootFile(afile, f"{adir}/{sub}/{den}", rebin=None) + hist_num = cached_histos[f"{sub}/{num}"] + hist_den = cached_histos[f"{sub}/{den}"] + + if props['rebin'] is not None: + hist_num = hist_num.Clone(f"{sub}/{num}" + "_clone") + hist_den = hist_den.Clone(f"{sub}/{den}" + "_clone") + hist_num.SetDirectory(0) # detach from file + hist_den.SetDirectory(0) # detach from file + + if isinstance(props['rebin'], (int, float)): + hist_num = hist_num.Rebin(int(props['rebin']), f"{sub}/{num}" + "_rebin") + hist_den = hist_den.Rebin(int(props['rebin']), f"{sub}/{den}" + "_rebin") + elif hasattr(props['rebin'], '__iter__'): + bin_edges_c = array.array('d', props['rebin']) + hist_num = hist_num.Rebin(len(bin_edges_c) - 1, f"{sub}/{num}" + "_rebin", bin_edges_c) + hist_den = hist_den.Rebin(len(bin_edges_c) - 1, f"{sub}/{den}" + "_rebin", bin_edges_c) + else: + raise ValueError(f"Unknown type for rebin: {type(props['rebin'])}") nbins, bin_edges, bin_centers, bin_widths = define_bins(hist_num) num_vals, num_errors = histo_values_errors(hist_num) @@ -242,8 +282,7 @@ def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix='' plotter = Plotter(args.sample_label, grid_color=None) ax2 = plotter.ax.twinx() eff_color = '#bd1f01' - ax2.set_ylabel('Efficiency', color=eff_color) - + valuesList, errorsList = [], [] colors_iter = iter(('black', 'blue')) for avar in vars1d: @@ -261,8 +300,18 @@ def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix='' if 'Eff' not in name and doNormalize: errors /= sum(values) values /= sum(values) + + if any(x in name for x in ('Eff', 'Fake', 'Duplicate', 'Merge')): + if 'Eff' in name: + axis_name = 'Efficiency' + elif 'Fake' in name: + axis_name = 'Fake Rate' + elif 'Dup' in name: + axis_name = 'Duplicate Rate' + elif 'Merge' in name: + axis_name = 'Merge Rate' + ax2.set_ylabel(axis_name, color=eff_color) - if 'Eff' in name: # ax2.set_yscale('log') ax2.set_ylim(-0.05, 1.15) @@ -299,31 +348,49 @@ def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix='' plt.tight_layout() plotter.save( os.path.join(outdir, name) ) -def plot1Dvars(afile, adir, avars, outdir, text, top_text=False): - for var, (xlabel, ylabel, rebin, logy, _) in avars.items(): - plotter = Plotter(args.sample_label) - root_hist = checkRootFile(afile, f"{adir}/{var}", rebin=rebin) - nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) - values, errors = histo_values_errors(root_hist) - errors /= 2 +def plot2D(h, props, outname): + """ + Plot with mplhep's hist2d (preserves ROOT bin edges, color bar included) + empty bins will be invisible (background color). + """ + plotter = Plotter(args.sample_label, fontsize=15) - plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, - fmt='s', color='black', label=var, **errorbar_kwargs) - plotter.ax.stairs(values, bin_edges, color='black', linewidth=2, baseline=None) + nbins_x, nbins_y, x_edges, y_edges = define_bins_2D(h) + values = histo_values_2D(h) + + pcm = plotter.ax.pcolormesh(x_edges, y_edges, np.where(values==0, np.nan, values), + cmap='viridis', shading='auto') + + plotter.labels(x=props['xtitle'], y=props['ytitle']) + plotter.fig.colorbar(pcm, ax=plotter.ax, label='# Clusters') + plt.tight_layout() + plotter.save(outname) + +# def plot1D(afile, adir, avars, outdir, text, top_text=False): +# for var, (xlabel, ylabel, rebin, logy, _) in avars.items(): +# plotter = Plotter(args.sample_label) +# root_hist = checkRootFile(afile, f"{adir}/{var}", rebin=rebin) +# nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) +# values, errors = histo_values_errors(root_hist) +# errors /= 2 - plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, - verticalalignment='top', horizontalalignment='left') +# plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, +# fmt='s', color='black', label=var, **errorbar_kwargs) +# plotter.ax.stairs(values, bin_edges, color='black', linewidth=2, baseline=None) - plotter.limits_with_margin(values, errors, logY=logy) - plotter.labels(x=xlabel, y=ylabel) +# plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, +# verticalalignment='top', horizontalalignment='left') - if top_text: - plotter.ax.text(0.97, 0.97, root_hist.GetTitle().replace('ET', r'$E_T$'), - transform=plotter.ax.transAxes, fontsize=fontsize, - verticalalignment='top', horizontalalignment='right') +# plotter.limits_with_margin(values, errors, logY=logy) +# plotter.labels(x=xlabel, y=ylabel) - plt.tight_layout() - plotter.save( os.path.join(outdir, var, suffix) ) +# if top_text: +# plotter.ax.text(0.97, 0.97, root_hist.GetTitle().replace('ET', r'$E_T$'), +# transform=plotter.ax.transAxes, fontsize=fontsize, +# verticalalignment='top', horizontalalignment='right') + +# plt.tight_layout() +# plotter.save( os.path.join(outdir, var) ) if __name__ == '__main__': @@ -388,30 +455,47 @@ def __call__(self, parser, namespace, values, option_string=None): else: name = key.GetName() cached_histos[name] = key.ReadObj() - print("...done.") + print("### INFO: ...done.") - # subdirs = GetRootSubDir(afile, dqm_dir) for subdir in subdirs: checkRootDir(afile, f"{dqm_dir}/{subdir}") createDir(f'{args.odir}/{subdir}') + createIndexPHP(src=args.odir, dest=f'{args.odir}/{subdir}') + for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')): vars1D = { - # PF tester producer - f'SimClusters{suf}En': ('Energy [GeV]', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersEn': ('Energy [GeV]', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Eff_vs_Energy{name}': ('Energy [GeV]', nEventsLabel, None, True, False, None), - f'SimClusters{suf}Pt': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersPt': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Eff_vs_Pt{name}': (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, None), - f'SimClusters{suf}Eta': (r'$\eta$', nEventsLabel, None, False, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersEta': (r'$\eta$', nEventsLabel, None, False, False, 'Reco'), - f'{subdir}/Eff_vs_Eta{name}': (r'$\eta$', nEventsLabel, None, False, False, None), - f'SimClusters{suf}Phi': (r'$\phi$', nEventsLabel, None, False, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersPhi': (r'$\phi$', nEventsLabel, None, False, False, 'Reco'), - f'{subdir}/Eff_vs_Phi{name}': (r'$\phi$', nEventsLabel, None, False, False, None), - f'SimClusters{suf}Mult': ('Multiplicity', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersMult': ('Multiplicity', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Eff_vs_Mult{name}': ('Multiplicity', nEventsLabel, None, True, False, None), + # Cluster efficiency + f'SimClusters{suf}En' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersEn' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Eff_vs_En{name}' : ('Energy [GeV]', nEventsLabel, None, True, False, None), + f'SimClusters{suf}Pt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersPt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Eff_vs_Pt{name}' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, None), + f'SimClusters{suf}Eta' : (r'$\eta$', nEventsLabel, None, False, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersEta' : (r'$\eta$', nEventsLabel, None, False, False, 'Reco'), + f'{subdir}/Eff_vs_Eta{name}' : (r'$\eta$', nEventsLabel, None, False, False, None), + f'SimClusters{suf}Phi' : (r'$\phi$', nEventsLabel, None, False, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersPhi' : (r'$\phi$', nEventsLabel, None, False, False, 'Reco'), + f'{subdir}/Eff_vs_Phi{name}' : (r'$\phi$', nEventsLabel, None, False, False, None), + f'SimClusters{suf}Mult' : ('Multiplicity', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/SimClustersMatchedRecoClustersMult' : ('Multiplicity', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Eff_vs_Mult{name}' : ('Multiplicity', nEventsLabel, None, True, False, None), + # Cluster fake rate + f'RecoClusters{suf}En' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/RecoClustersMatchedSimClustersEn' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Fake_vs_En{name}' : ('Energy [GeV]', nEventsLabel, None, True, False, None), + f'RecoClusters{suf}Pt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/RecoClustersMatchedSimClustersPt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Fake_vs_Pt{name}' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, None), + f'RecoClusters{suf}Eta' : (r'$\eta$', nEventsLabel, None, False, False, 'Sim'), + f'{subdir}/RecoClustersMatchedSimClustersEta' : (r'$\eta$', nEventsLabel, None, False, False, 'Reco'), + f'{subdir}/Fake_vs_Eta{name}' : (r'$\eta$', nEventsLabel, None, False, False, None), + f'RecoClusters{suf}Phi' : (r'$\phi$', nEventsLabel, None, False, False, 'Sim'), + f'{subdir}/RecoClustersMatchedSimClustersPhi' : (r'$\phi$', nEventsLabel, None, False, False, 'Reco'), + f'{subdir}/Fake_vs_Phi{name}' : (r'$\phi$', nEventsLabel, None, False, False, None), + f'RecoClusters{suf}Mult' : ('Multiplicity', nEventsLabel, None, True, False, 'Sim'), + f'{subdir}/RecoClustersMatchedSimClustersMult' : ('Multiplicity', nEventsLabel, None, True, False, 'Reco'), + f'{subdir}/Fake_vs_Mult{name}' : ('Multiplicity', nEventsLabel, None, True, False, None), } # Compare pairs of variables @@ -420,7 +504,7 @@ def __call__(self, parser, namespace, values, option_string=None): avars = (var, next(it), next(it)) # reco, sim and efficiency for a given variable plotEffComp1D(cached_histos, vars1d=avars, outdir=args.odir, text='', suffix=f'') - titles = {'response': r"$$", 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", 'eff': 'Efficiency'} + titles = {'response': r"$p_{T}^{Reco}/p_{T}^{Sim}$", 'av_response': r"$$", 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", 'eff': 'Efficiency'} varsOverlay = { "ResponsePt_Mean" : dict(ytitle=titles['response'], xtitle=r'$p_{T} [GeV]$', logy=False), @@ -433,13 +517,27 @@ def __call__(self, parser, namespace, values, option_string=None): "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], xtitle='Multiplicity', logy=False), } for name, props in varsOverlay.items(): - plotOverlay(subdirs, dqm_dir, name, props, outdir=args.odir) + plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) varsResponse = { - ("ResponsePt_Sigma", "ResponsePt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', logy=False), - ("ResponseEta_Sigma", "ResponseEta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', logy=False), - ("ResponsePhi_Sigma", "ResponsePhi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', logy=False), - ("ResponseMult_Sigma", "ResponseMult_Mean") : dict(name='ResolutionMult',ytitle=titles['resolution'], xtitle='Multiplicity', logy=False), + ("ResponsePt_Sigma", "ResponsePt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', rebin=4, logy=False), + ("ResponseEta_Sigma", "ResponseEta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', rebin=4, logy=False), + ("ResponsePhi_Sigma", "ResponsePhi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', rebin=None, logy=False), + ("ResponseMult_Sigma", "ResponseMult_Mean") : dict(name='ResolutionMult',ytitle=titles['resolution'], xtitle='Multiplicity', rebin=None, logy=False), } for (num, den), props in varsResponse.items(): - plotOverlayRatio(subdirs, dqm_dir, num, den, props, outdir=args.odir) + plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir=args.odir) + + vars2D = { + 'ResponsePt' : dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)), + 'ResponseEta' : dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)), + 'ResponsePhi' : dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -2., -1., 0., 1., 2., 3.15)), + 'ResponseMult': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 45., 100., 200.)), + } + + for name, props in vars2D.items(): + plotter = Plotter(args.sample_label, fontsize=15) + for subdir in subdirs: + root_hist = cached_histos[f"{subdir}/{name}"] + plotProject(root_hist, props, rebin_edges=props['rebin'], outname=os.path.join(args.odir, subdir, name + '_Projected')) + plot2D(root_hist, props, outname=os.path.join(args.odir, subdir, name)) From a8ace6a7dedb03b3152235ff609515ca49532323 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Fri, 24 Oct 2025 09:57:43 +0200 Subject: [PATCH 04/64] Redefine eta and energy for SimClusters, add cut on energy fraction and SimTrack pt --- .../RecoParticleFlow/plugins/PFTester.cc | 419 ++++++++++++++---- .../python/hltPFPostProcessor_cfi.py | 46 +- .../python/hltPFValidation_cfi.py | 44 +- .../scripts/makeHLTPFValidationPlots.py | 234 ++++++---- 4 files changed, 559 insertions(+), 184 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index d63abe20f67a0..8c5cefa949cf9 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -14,6 +14,7 @@ #include "DataFormats/ParticleFlowReco/interface/PFRecTrack.h" #include "DataFormats/ParticleFlowReco/interface/PFCluster.h" #include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" +#include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" #include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" #include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" #include "SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h" @@ -31,6 +32,7 @@ class PFTester : public DQMEDAnalyzer { edm::EDGetTokenT PFCandToken_; edm::EDGetTokenT PFClusterToken_; + edm::EDGetTokenT CaloParticleToken_; edm::EDGetTokenT SimClusterToken_; edm::EDGetTokenT> RecoToSimAssociatorToken_; edm::EDGetTokenT> SimToRecoAssociatorToken_; @@ -58,7 +60,9 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_TrackNumMeasurements_; MonitorElement* h_TrackImpactParameter_; - MonitorElement* h_NumPFClusters_; + MonitorElement* h_CaloParticleToSimClusterEnergyFraction_; + MonitorElement* h_CaloParticleToSimHitsEnergyFraction_; + MonitorElement* h_PFClusterE_; MonitorElement* h_PFClusterEta_; MonitorElement* h_PFClusterPhi_; @@ -67,16 +71,43 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_PFClusterType_; MonitorElement* h_PFClusterHitFraction_; MonitorElement* h_PFClusterHitDetId_; - - MonitorElement* h_simToRecoScore_; + + MonitorElement* h_nPFClusters_; + MonitorElement* h_nSimClusters_; + MonitorElement* h_nSimClustersPrimary_; MonitorElement* h_recoToSimScore_; + MonitorElement* h_simToRecoScore_; + MonitorElement* h_simToRecoShEnF_; + MonitorElement* h_simToRecoShEnF_Score_; + MonitorElement* h_simToRecoShEnF_En_; + MonitorElement* h_simToRecoShEnF_EnHits_; + MonitorElement* h_simToRecoShEnF_EnFrac_; + MonitorElement* h_simToRecoShEnF_Mult_; + MonitorElement* h_simToRecoScore_En_; + MonitorElement* h_simToRecoScore_EnHits_; + MonitorElement* h_simToRecoScore_EnFrac_; + MonitorElement* h_simToRecoScore_Mult_; + MonitorElement* h_SimTrackToSimHitsEnergyFraction_; std::vector assocScoreThresholds_; + uint nAssocScoreThresholds_; + double enFracCut_; + double ptCut_; - const std::unordered_map> histoVars = { - {"En", std::make_tuple(100, 0., 50.)}, + const std::unordered_map> histoVarsReco = { + {"En", std::make_tuple(100, 0., 100.)}, {"Pt", std::make_tuple(200, 0., 100.)}, - {"PtLow", std::make_tuple(100, 0., 10.)}, + {"PtLow", std::make_tuple(100, 0., 10.)}, + {"Eta", std::make_tuple(50, -6.5, 6.5)}, + {"Phi", std::make_tuple(50, -3.5, 3.5)}, + {"Mult", std::make_tuple(200, 0., 200.)}, + }; + const std::unordered_map> histoVarsSim = { + {"En", std::make_tuple(100, 0., 100.)}, + {"EnHits", std::make_tuple(100, 0., 100.)}, + {"EnFrac", std::make_tuple(220, 0., 1.1)}, + {"Pt", std::make_tuple(200, 0., 100.)}, + {"PtLow", std::make_tuple(100, 0., 10.)}, {"Eta", std::make_tuple(50, -6.5, 6.5)}, {"Phi", std::make_tuple(50, -3.5, 3.5)}, {"Mult", std::make_tuple(200, 0., 200.)}, @@ -86,17 +117,34 @@ class PFTester : public DQMEDAnalyzer { using VUMap = std::vector; UMap h_simClusters_; UMap h_simClustersReconstructable_; - VUMap h_simClustersMatchedRecoClusters_{histoVars.size()}; - VUMap h_simClustersMultiMatchedRecoClusters_{histoVars.size()}; + VUMap h_simClustersMatchedRecoClusters_; + VUMap h_simClustersMultiMatchedRecoClusters_; UMap h_recoClusters_; UMap h_recoClustersReconstructable_; - VUMap h_recoClustersMatchedSimClusters_{histoVars.size()}; - VUMap h_recoClustersMultiMatchedSimClusters_{histoVars.size()}; + VUMap h_recoClustersMatchedSimClusters_; + VUMap h_recoClustersMultiMatchedSimClusters_; + + std::unordered_map> histo2dVarsReco = { + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, + {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, + {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, + {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, + {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + }; - std::unordered_map> histo2dVars = { - {"En_Eta", std::make_tuple(100, 0., 50., 50, -6.5, 6.5)}, - {"En_Phi", std::make_tuple(100, 0., 50., 50, -3.5, 3.5)}, - {"En_Mult", std::make_tuple(100, 0., 50., 200, 0., 200.)}, + std::unordered_map> histo2dVarsSim = { + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"EnHits_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"EnHits_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"EnHits_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"EnFrac_Eta", std::make_tuple(220, 0., 1.1, 50, -6.5, 6.5)}, + {"EnFrac_Phi", std::make_tuple(220, 0., 1.1, 50, -3.5, 3.5)}, + {"EnFrac_Mult", std::make_tuple(220, 0., 1.1, 200, 0., 200.)}, {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, @@ -108,17 +156,18 @@ class PFTester : public DQMEDAnalyzer { using VU2Map = std::vector>; U2Map h2d_simClusters_; U2Map h2d_simClustersReconstructable_; - VU2Map h2d_simClustersMatchedRecoClusters_{histo2dVars.size()}; + VU2Map h2d_simClustersMatchedRecoClusters_; U2Map h2d_recoClusters_; U2Map h2d_recoClustersReconstructable_; - VU2Map h2d_recoClustersMatchedSimClusters_{histo2dVars.size()}; + VU2Map h2d_recoClustersMatchedSimClusters_; - VU2Map h2d_response_{histoVars.size()}; + VU2Map h2d_response_; }; PFTester::PFTester(const edm::ParameterSet& iConfig) : PFCandToken_(consumes(iConfig.getParameter("PFCand"))), PFClusterToken_(consumes(iConfig.getParameter("PFCluster"))), + CaloParticleToken_(consumes(iConfig.getParameter("CaloParticle"))), SimClusterToken_(consumes(iConfig.getParameter("SimCluster"))), RecoToSimAssociatorToken_(consumes>( iConfig.getParameter("PFClusterSimClusterAssociator"))), @@ -128,7 +177,18 @@ PFTester::PFTester(const edm::ParameterSet& iConfig) iConfig.getParameter("PFClusterCaloParticleAssociator"))), CpToRecoAssociatorToken_(consumes>( iConfig.getParameter("PFClusterCaloParticleAssociator"))), - assocScoreThresholds_(iConfig.getParameter>("assocScoreThresholds")) {} + assocScoreThresholds_(iConfig.getParameter>("assocScoreThresholds")), + enFracCut_(iConfig.getParameter("enFracCut")), + ptCut_(iConfig.getParameter("ptCut")) { + nAssocScoreThresholds_ = assocScoreThresholds_.size(); + h_simClustersMatchedRecoClusters_.resize(nAssocScoreThresholds_); + h_simClustersMultiMatchedRecoClusters_.resize(nAssocScoreThresholds_); + h_recoClustersMatchedSimClusters_.resize(nAssocScoreThresholds_); + h_recoClustersMultiMatchedSimClusters_.resize(nAssocScoreThresholds_); + h2d_simClustersMatchedRecoClusters_.resize(nAssocScoreThresholds_); + h2d_recoClustersMatchedSimClusters_.resize(nAssocScoreThresholds_); + h2d_response_.resize(nAssocScoreThresholds_); +} void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { ibook.setCurrentFolder("HLT/ParticleFlow/PFCandidates"); @@ -155,8 +215,11 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_TrackNumMeasurements_ = ibook.book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); h_TrackImpactParameter_ = ibook.book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); + ibook.setCurrentFolder("HLT/ParticleFlow/CaloParticles"); + h_CaloParticleToSimClusterEnergyFraction_ = ibook.book1D("CaloParticleToSimClusterEnergyFraction", "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", 100, 0, 2); + h_CaloParticleToSimHitsEnergyFraction_ = ibook.book1D("CaloParticleToSimHitsEnergyFraction", "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", 100, 0, 2); + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusters"); - h_NumPFClusters_ = ibook.book1D("NumPFClusters", "Number of PFClusters per PFCandidate", 25, 0, 25); h_PFClusterE_ = ibook.book1D("PFClusterE", "PFCluster Energy;E [GeV]", 100, 0, 100); h_PFClusterEta_ = ibook.book1D("PFClusterEta", "PFCluster Eta;#eta", 120, -6, 6); h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "PFCluster Phi;#phi", 128, -3.2, 3.2); @@ -167,26 +230,38 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_PFClusterHitDetId_ = ibook.book1D("PFClusterHitDetId", "PFCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); - h_simToRecoScore_ = ibook.book1D("simToRecoScore", "simToRecoScore;Sim #rightarrow Reco score", 50, 0, 1); + std::string pfValidFolder = "HLT/ParticleFlow/PFClusterValidation_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_); + ibook.setCurrentFolder(pfValidFolder); + h_nSimClusters_ = ibook.book1D("nSimClusters", "Number of SimClusters;Number of SimClusters per event", 100, 0, 100); + h_nSimClustersPrimary_ = ibook.book1D("nSimClustersPrimary", "Number of Primary SimClusters;Number of Primary SimClusters per event", 100, 0, 100); + h_nPFClusters_ = ibook.book1D("nPFClusters", "Number of PFClusters per PFCandidate", 100, 0, 100); h_recoToSimScore_ = ibook.book1D("recoToSimScore", "recoToSimScore;Reco #rightarrow Sim score", 50, 0, 1); - - for (auto& hVar : histoVars) { + h_simToRecoScore_ = ibook.book1D("simToRecoScore", "simToRecoScore;Sim #rightarrow Reco score", 50, 0, 1); + h_simToRecoShEnF_ = ibook.book1D("simToRecoShEnF", "simToRecoSharedEnergy;Sim #rightarrow Reco shared energy fraction", 50, 0, 2); + h_simToRecoShEnF_Score_ = ibook.book2D("simToRecoShEnF_Score", "simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 50, 0, 2, 50, 0, 2); + h_simToRecoShEnF_En_ = ibook.book2D("simToRecoShEnF_En", "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy", 50, 0, 2, 100, 0., 100.); + h_simToRecoShEnF_EnHits_ = ibook.book2D("simToRecoShEnF_EnHits", "simToRecoSharedEnergy vs Energy Hits;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", 50, 0, 2, 100, 0., 100.); + h_simToRecoShEnF_EnFrac_ = ibook.book2D("simToRecoShEnF_EnFrac", "simToRecoSharedEnergy vs Energy Fraction;Sim #rightarrow Reco shared energy fraction;EnFrac", 50, 0, 2, 220, 0., 1.1); + h_simToRecoShEnF_Mult_ = ibook.book2D("simToRecoShEnF_Mult", "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", 50, 0, 2, 200, 0., 200.); + h_simToRecoScore_En_ = ibook.book2D("simToRecoScore_En", "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy", 50, 0, 2, 100, 0., 100.); + h_simToRecoScore_EnHits_ = ibook.book2D("simToRecoScore_EnHits", "simToRecoScore vs Energy Hits;Sim #rightarrow Reco score;Energy_{hits}", 50, 0, 2, 100, 0., 100.); + h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", 50, 0, 2, 220, 0., 1.1); + h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 50, 0, 2, 200, 0., 200.); + h_SimTrackToSimHitsEnergyFraction_ = ibook.book1D("SimTrackToSimHitsEnergyFraction", "SimTrackToSimHitsEnergyFraction;SimTrack to SimHits energy fraction", 110, 0, 1.1); + + for (auto& hVar : histoVarsSim) { auto [nBins, hMin, hMax] = hVar.second; - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation"); + ibook.setCurrentFolder(pfValidFolder); h_simClusters_[hVar.first] = ibook.book1D("SimClusters" + hVar.first, "SimClusters;" + hVar.first, nBins, hMin, hMax); h_simClustersReconstructable_[hVar.first] = ibook.book1D( "SimClustersReconstructable" + hVar.first, "SimClustersReconstructable;" + hVar.first, nBins, hMin, hMax); - h_recoClusters_[hVar.first] = - ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); - h_recoClustersReconstructable_[hVar.first] = ibook.book1D( - "RecoClustersReconstructable" + hVar.first, "RecoClustersReconstructable;" + hVar.first, nBins, hMin, hMax); - for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation/" + threshStr); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); h_simClustersMatchedRecoClusters_[ithr][hVar.first] = ibook.book1D("SimClustersMatchedRecoClusters" + hVar.first, "SimClusters matched to RecoClusters;" + hVar.first, @@ -199,7 +274,22 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev nBins, hMin, hMax); + } + } + + for (auto& hVar : histoVarsReco) { + auto [nBins, hMin, hMax] = hVar.second; + ibook.setCurrentFolder(pfValidFolder); + h_recoClusters_[hVar.first] = + ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); + h_recoClustersReconstructable_[hVar.first] = ibook.book1D( + "RecoClustersReconstructable" + hVar.first, "RecoClustersReconstructable;" + hVar.first, nBins, hMin, hMax); + + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); h_recoClustersMatchedSimClusters_[ithr][hVar.first] = ibook.book1D("RecoClustersMatchedSimClusters" + hVar.first, "RecoClusters matched to SimClusters;" + hVar.first, @@ -215,10 +305,10 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev } } - for (auto& h2dVar : histo2dVars) { + for (auto& h2dVar : histo2dVarsSim) { auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation"); + ibook.setCurrentFolder(pfValidFolder); auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_simClusters_[h2dVar.first] = ibook.book2D("SimClusters" + h2dVar.first, @@ -238,6 +328,28 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev nBinsY, hMinY, hMaxY); + + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + h2d_simClustersMatchedRecoClusters_[ithr][h2dVar.first] = + ibook.book2D("SimClustersMatchedRecoClusters" + h2dVar.first, + "SimClusters matched to RecoClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + } + } + + for (auto& h2dVar : histo2dVarsReco) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + + ibook.setCurrentFolder(pfValidFolder); + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_recoClusters_[h2dVar.first] = ibook.book2D("RecoClusters" + h2dVar.first, "RecoClusters;" + x_title + ";" + y_title, nBinsX, @@ -256,19 +368,10 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev hMinY, hMaxY); - for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation/" + threshStr); - h2d_simClustersMatchedRecoClusters_[ithr][h2dVar.first] = - ibook.book2D("SimClustersMatchedRecoClusters" + h2dVar.first, - "SimClusters matched to RecoClusters;" + x_title + ";" + y_title, - nBinsX, - hMinX, - hMaxX, - nBinsY, - hMinY, - hMaxY); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); h2d_recoClustersMatchedSimClusters_[ithr][h2dVar.first] = ibook.book2D("RecoClustersMatchedSimClusters" + h2dVar.first, "RecoClusters matched to SimClusters;" + x_title + ";" + y_title, @@ -281,11 +384,11 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev } } - for (auto& hVar : histoVars) { + for (auto& hVar : histoVarsSim) { auto [nBins, hMin, hMax] = hVar.second; - for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusterValidation/" + threshStr); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); h2d_response_[ithr][hVar.first] = ibook.book2D("Response" + hVar.first, "Response;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); } @@ -311,6 +414,18 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { return; } + // -------------------------------------------------------------------- + // ---------------- Calo Particles ------------------------------------ + // -------------------------------------------------------------------- + + edm::Handle CaloParticle; + iEvent.getByToken(CaloParticleToken_, CaloParticle); + if (!CaloParticle.isValid()) { + edm::LogInfo("PFTester") << "Input CaloParticle collection not found."; + return; + } + auto CaloParticles = *CaloParticle; + // -------------------------------------------------------------------- // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- @@ -338,7 +453,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { return; } auto simToRecoAssoc = *SimToRecoAssociatorCollection; - // std::cout << "simRecColl size : " << simToRecoAssoc.size() << std::endl; edm::Handle> RecoToSimAssociatorCollection; iEvent.getByToken(RecoToSimAssociatorToken_, RecoToSimAssociatorCollection); @@ -348,33 +462,89 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { } auto recoToSimAssoc = *RecoToSimAssociatorCollection; + // -------------------------------------------------------------------- + // ----- Calo Particle plots ------------ + // -------------------------------------------------------------------- + uint nSimClustersFromCPs = 0; + std::unordered_map simClusterToCPEnergyMap; + for (unsigned int cpId = 0; cpId < CaloParticles.size(); ++cpId) { + double energySumSimClusters = 0; + double energySumSimHits = 0; + nSimClustersFromCPs += CaloParticles[cpId].simClusters().size(); + for (const auto& scRef : CaloParticles[cpId].simClusters()) { + auto const& sc = *(scRef); + energySumSimClusters += sc.energy(); + for (auto hit_energy : sc.hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; + } + #ifdef debug + LogDebug("PFTester") << " CaloParticles[" << cpId << "].energy()=" << CaloParticles[cpId].energy() + << ", energySumSimClusters=" << energySumSimClusters + << ", energySumSimHits=" << energySumSimHits << std::endl; + #endif + h_CaloParticleToSimClusterEnergyFraction_->Fill(energySumSimClusters/CaloParticles[cpId].energy()); + h_CaloParticleToSimHitsEnergyFraction_->Fill(energySumSimHits/CaloParticles[cpId].energy()); + } + // -------------------------------------------------------------------- // ----- Efficiency and merge computation at cluster level ------------ // -------------------------------------------------------------------- + + uint nSimClusters = 0; + uint nSimClustersPrimary = 0; std::vector recoIdsMerged; - for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + + double energySumSimHits = 0; + for (auto hit_energy : simClusters[simId].hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simClusters[simId].energy()); + + // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) + double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; + if (SimClusterToCPEnergyFraction < enFracCut_) + continue; + // apply cut on pt of the sim track + if (simClusters[simId].pt() < ptCut_) + continue; + // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel auto const scTrack = simClusters[simId].g4Tracks()[0]; const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); - if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel + auto const simTrackEtaAtBoundary = pos.Eta(); + if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel continue; + ++nSimClusters; + if (simClusters[simId].g4Tracks()[0].isPrimary()) + ++nSimClustersPrimary; + h_simClusters_["En"]->Fill(simClusters[simId].energy()); + h_simClusters_["EnHits"]->Fill(energySumSimHits); + h_simClusters_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClusters_["Pt"]->Fill(simClusters[simId].pt()); - h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); - h_simClusters_["Eta"]->Fill(simClusters[simId].eta()); + h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); + h_simClusters_["Eta"]->Fill(simTrackEtaAtBoundary); h_simClusters_["Phi"]->Fill(simClusters[simId].phi()); h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); - h2d_simClusters_["En_Eta"]->Fill(simClusters[simId].energy(), simClusters[simId].eta()); + h2d_simClusters_["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); h2d_simClusters_["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); h2d_simClusters_["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); - h2d_simClusters_["Pt_Eta"]->Fill(simClusters[simId].pt(), simClusters[simId].eta()); + h2d_simClusters_["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); + h2d_simClusters_["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClusters_["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); + h2d_simClusters_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); + h2d_simClusters_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); + h2d_simClusters_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClusters_["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClusters_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClusters_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); - h2d_simClusters_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].eta()); + h2d_simClusters_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); h2d_simClusters_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); const edm::Ref simClusterRef(SimCluster, simId); @@ -386,30 +556,53 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { continue; h_simClustersReconstructable_["En"]->Fill(simClusters[simId].energy()); + h_simClustersReconstructable_["EnHits"]->Fill(energySumSimHits); + h_simClustersReconstructable_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClustersReconstructable_["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersReconstructable_["PtLow"]->Fill(simClusters[simId].pt()); - h_simClustersReconstructable_["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersReconstructable_["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersReconstructable_["Eta"]->Fill(simTrackEtaAtBoundary); h_simClustersReconstructable_["Phi"]->Fill(simClusters[simId].phi()); h_simClustersReconstructable_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["En_Eta"]->Fill(simClusters[simId].energy(), simClusters[simId].eta()); + h2d_simClustersReconstructable_["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); h2d_simClustersReconstructable_["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); h2d_simClustersReconstructable_["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["Pt_Eta"]->Fill(simClusters[simId].pt(), simClusters[simId].eta()); + h2d_simClustersReconstructable_["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); + h2d_simClustersReconstructable_["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClustersReconstructable_["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); + h2d_simClustersReconstructable_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); + h2d_simClustersReconstructable_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); + h2d_simClustersReconstructable_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClustersReconstructable_["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClustersReconstructable_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClustersReconstructable_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].eta()); + h2d_simClustersReconstructable_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); h2d_simClustersReconstructable_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); - std::vector wasNotFilled(assocScoreThresholds_.size(), true); + std::vector wasNotFilled(nAssocScoreThresholds_, true); for (const auto& recoPair : simToRecoMatched) { const auto recoPairIdx = recoPair.first.index(); - for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; - h_simToRecoScore_->Fill(recoPair.second.second); - if (recoPair.second.second > thresh) + auto score = recoPair.second.second; + auto shared_energy = recoPair.second.first; + auto shared_energy_frac = shared_energy / energySumSimHits; + + h_simToRecoScore_->Fill(score); + h_simToRecoShEnF_->Fill(shared_energy_frac); + h_simToRecoShEnF_Score_->Fill(shared_energy_frac, score); + h_simToRecoShEnF_En_->Fill(shared_energy_frac, simClusters[simId].energy()); + h_simToRecoShEnF_EnHits_->Fill(shared_energy_frac, energySumSimHits); + h_simToRecoShEnF_EnFrac_->Fill(shared_energy_frac, SimClusterToCPEnergyFraction); + h_simToRecoShEnF_Mult_->Fill(shared_energy_frac, simClusters[simId].numberOfRecHits()); + h_simToRecoScore_En_->Fill(score, simClusters[simId].energy()); + h_simToRecoScore_EnHits_->Fill(score, energySumSimHits); + h_simToRecoScore_EnFrac_->Fill(score, SimClusterToCPEnergyFraction); + h_simToRecoScore_Mult_->Fill(score, simClusters[simId].numberOfRecHits()); + + if (score > thresh) continue; // numerator histograms must be filled only once per sim cluster @@ -417,24 +610,38 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (wasNotFilled[ithr]) { wasNotFilled[ithr] = false; h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); + h_simClustersMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); + h_simClustersMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); - h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(simClusters[simId].energy(), - simClusters[simId].eta()); + simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simClusters[simId].eta()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Eta"]->Fill(energySumSimHits, + simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Phi"]->Fill(energySumSimHits, + simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Mult"]->Fill(energySumSimHits, + simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, + simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, + simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, + simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), - simClusters[simId].eta()); + simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); } @@ -461,32 +668,39 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (nSimMerged > 1) { h_simClustersMultiMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); + h_simClustersMultiMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); + h_simClustersMultiMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClustersMultiMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); - h_simClustersMultiMatchedRecoClusters_[ithr]["Eta"]->Fill(simClusters[simId].eta()); + h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); h_simClustersMultiMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); h_simClustersMultiMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); } - // for (const auto& recoPair : simToRecoMatched) { - // std::cout << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() - // << " shared energy = " << recoPair.second.first - // << " score = " << recoPair.second.second << std::endl; - // } + #ifdef debug + for (const auto& recoPair : simToRecoMatched) { + LogDebug("PFTester") << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() + << " shared energy = " << recoPair.second.first + << " score = " << recoPair.second.second << std::endl; + } + #endif } } } + h_nSimClusters_->Fill(nSimClusters); + h_nSimClustersPrimary_->Fill(nSimClustersPrimary); std::vector simIdsDuplicates; // -------------------------------------------------------------------- // ----- Fakes and duplicates computation at cluster level ------------ // -------------------------------------------------------------------- + h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); @@ -510,7 +724,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { h_recoClustersReconstructable_["En"]->Fill(recoClusters[recoId].energy()); h_recoClustersReconstructable_["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersReconstructable_["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClustersReconstructable_["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersReconstructable_["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersReconstructable_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersReconstructable_["Mult"]->Fill(recoClusters[recoId].size()); @@ -524,11 +738,27 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { h2d_recoClustersReconstructable_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClustersReconstructable_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - std::vector wasNotFilled(assocScoreThresholds_.size(), true); + std::vector wasNotFilled(nAssocScoreThresholds_, true); for (const auto& simPair : recoToSimMatched) { const auto simPairIdx = simPair.first.index(); - // std::cout << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx - // << " score = " << simPair.second << std::endl; + + #ifdef debug + LogDebug("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx + << " score = " << simPair.second << std::endl; + #endif + + double energySumSimHits = 0; + for (auto hit_energy : simClusters[simPairIdx].hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + + // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) + double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simPairIdx]; + if (SimClusterToCPEnergyFraction < enFracCut_) + continue; + // apply cut on pt of the sim track + if (simClusters[simPairIdx].pt() < ptCut_) + continue; // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel @@ -537,7 +767,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel continue; - for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; h_recoToSimScore_->Fill(simPair.second); @@ -549,7 +779,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { wasNotFilled[ithr] = false; h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); @@ -594,7 +824,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (nRecoDuplicates > 1) { h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); h_recoClustersMultiMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); @@ -609,11 +839,25 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { // -------------------------------------------------------------------- for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + + double energySumSimHits = 0; + for (auto hit_energy : simClusters[simId].hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) + double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; + if (SimClusterToCPEnergyFraction < enFracCut_) + continue; + // apply cut on pt of the sim track + if (simClusters[simId].pt() < ptCut_) + continue; + // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel auto const scTrack = simClusters[simId].g4Tracks()[0]; const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); - if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel + auto const simTrackEtaAtBoundary = pos.Eta(); + if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel continue; const edm::Ref simClusterRef(SimCluster, simId); @@ -624,7 +868,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (simToRecoMatched.empty()) continue; - for (unsigned ithr = 0; ithr < assocScoreThresholds_.size(); ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; bool fill = true; @@ -666,9 +910,13 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (fill) { h2d_response_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_response_[ithr]["EnHits"]->Fill(energySumSimHits, + recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_response_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, + recoClusters[recoId].pt() / simClusters[simId].pt()); h2d_response_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["Eta"]->Fill(simClusters[simId].eta(), + h2d_response_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].pt() / simClusters[simId].pt()); h2d_response_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].pt() / simClusters[simId].pt()); @@ -805,7 +1053,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { // Fill the Respective Elements Sizes h_NumElements_->Fill(numElements); - h_NumPFClusters_->Fill(numPFClusters); h_NumTrackElements_->Fill(numTrackElements); h_NumMuonElements_->Fill(numMuonElements); h_NumPS1Elements_->Fill(numPS1Elements); diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 5c595e0daab28..15e4db1faa333 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -6,28 +6,34 @@ hltPFClusterPostProcessor = DQMEDHarvester( "DQMGenericClient", - subDirs=cms.untracked.vstring("HLT/ParticleFlow/PFClusterValidation"), + subDirs=cms.untracked.vstring("HLT/ParticleFlow/PFClusterValidation*"), efficiency = cms.vstring( *[ item for thr in _thresholds for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) for item in ( - f"'Score{thr}/Eff_vs_EnEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", - f"'Score{thr}/Eff_vs_EnPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", - f"'Score{thr}/Eff_vs_EnMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", - f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", - f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", - f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", - f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", - f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", - f"'Score{thr}/Fake_vs_EnEta{name}' 'Fake Rate vs Energy-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClusters{suf}Pt_Eta fake", - f"'Score{thr}/Fake_vs_EnPhi{name}' 'Fake Rate vs Energy-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClusters{suf}Pt_Phi fake", - f"'Score{thr}/Fake_vs_EnMult{name}' 'Fake Rate vs Energy-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClusters{suf}Pt_Mult fake", - f"'Score{thr}/Fake_vs_PtEta{name}' 'Fake Rate vs p_{{T}}-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClusters{suf}Pt_Eta fake", - f"'Score{thr}/Fake_vs_PtPhi{name}' 'Fake Rate vs p_{{T}}-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClusters{suf}Pt_Phi fake", - f"'Score{thr}/Fake_vs_PtMult{name}' 'Fake Rate vs p_{{T}}-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClusters{suf}Pt_Mult fake", - f"'Score{thr}/Fake_vs_MultEta{name}' 'Fake Rate vs Mult-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Eta RecoClusters{suf}Mult_Eta fake", - f"'Score{thr}/Fake_vs_MultPhi{name}' 'Fake Rate vs Mult-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClusters{suf}Mult_Phi fake", + f"'Score{thr}/Eff_vs_EnEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", + f"'Score{thr}/Eff_vs_EnPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", + f"'Score{thr}/Eff_vs_EnMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", + f"'Score{thr}/Eff_vs_EnHitsEta{name}' 'Efficiency vs Hits Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Eta SimClusters{suf}EnHits_Eta", + f"'Score{thr}/Eff_vs_EnHitsPhi{name}' 'Efficiency vs Hits Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Phi SimClusters{suf}EnHits_Phi", + f"'Score{thr}/Eff_vs_EnHitsMult{name}' 'Efficiency vs Hits Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Mult SimClusters{suf}EnHits_Mult", + f"'Score{thr}/Eff_vs_EnFracEta{name}' 'Efficiency vs Energy fraction-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Eta SimClusters{suf}EnFrac_Eta", + f"'Score{thr}/Eff_vs_EnFracPhi{name}' 'Efficiency vs Energy fraction-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Phi SimClusters{suf}EnFrac_Phi", + f"'Score{thr}/Eff_vs_EnFracMult{name}' 'Efficiency vs Energy fraction-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Mult SimClusters{suf}EnFrac_Mult", + f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", + f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", + f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", + f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", + f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", + f"'Score{thr}/Fake_vs_EnEta{name}' 'Fake Rate vs Energy-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClusters{suf}Pt_Eta fake", + f"'Score{thr}/Fake_vs_EnPhi{name}' 'Fake Rate vs Energy-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClusters{suf}Pt_Phi fake", + f"'Score{thr}/Fake_vs_EnMult{name}' 'Fake Rate vs Energy-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClusters{suf}Pt_Mult fake", + f"'Score{thr}/Fake_vs_PtEta{name}' 'Fake Rate vs p_{{T}}-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClusters{suf}Pt_Eta fake", + f"'Score{thr}/Fake_vs_PtPhi{name}' 'Fake Rate vs p_{{T}}-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClusters{suf}Pt_Phi fake", + f"'Score{thr}/Fake_vs_PtMult{name}' 'Fake Rate vs p_{{T}}-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClusters{suf}Pt_Mult fake", + f"'Score{thr}/Fake_vs_MultEta{name}' 'Fake Rate vs Mult-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Eta RecoClusters{suf}Mult_Eta fake", + f"'Score{thr}/Fake_vs_MultPhi{name}' 'Fake Rate vs Mult-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClusters{suf}Mult_Phi fake", ) ], ), @@ -38,6 +44,8 @@ for item in ( # Efficiency f"'Score{thr}/Eff_vs_En{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Eff_vs_EnHits{name}' 'Efficiency vs Hits Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits SimClusters{suf}EnHits", + f"'Score{thr}/Eff_vs_EnFrac{name}' 'Efficiency vs Energy fraction {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", f"'Score{thr}/Eff_vs_Pt{name}' 'Efficiency vs p_{{T}} {suf}' Score{thr}/SimClustersMatchedRecoClustersPt SimClusters{suf}Pt", f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", @@ -56,6 +64,8 @@ f"'Score{thr}/Dup_vs_Mult{name}' 'Dup Rate vs Mult {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClusters{suf}Mult", # Merge rate f"'Score{thr}/Merge_vs_En{name}' 'Merge Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Merge_vs_EnHits{name}' 'Merge Rate vs Hits Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnHits SimClusters{suf}EnHits", + f"'Score{thr}/Merge_vs_EnFrac{name}' 'Merge Rate vs Energy fraction {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", f"'Score{thr}/Merge_vs_Pt{name}' 'Merge Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", f"'Score{thr}/Merge_vs_Eta{name}' 'Merge Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", f"'Score{thr}/Merge_vs_Phi{name}' 'Merge Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", @@ -69,6 +79,8 @@ for thr in _thresholds for item in ( f"'Score{thr}/ResponseEn' 'Response vs Energy' Score{thr}/ResponseEn rms", + f"'Score{thr}/ResponseEnHits' 'Response vs Hits Energy' Score{thr}/ResponseEnHits rms", + f"'Score{thr}/ResponseEnFrac' 'Response vs Energy fraction' Score{thr}/ResponseEnFrac rms", f"'Score{thr}/ResponsePt' 'Response vs p_{{T}}' Score{thr}/ResponsePt rms", f"'Score{thr}/ResponseEta' 'Response vs #eta' Score{thr}/ResponseEta rms", f"'Score{thr}/ResponsePhi' 'Response vs #phi' Score{thr}/ResponsePhi rms", diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 278bc0b077138..92012cf6b0c00 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -27,10 +27,49 @@ hltPFTesterECAL = cms.EDProducer("PFTester", PFCand = cms.InputTag("hltParticleFlowTmp"), PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + CaloParticle = cms.InputTag("mix","MergedCaloTruth"), SimCluster = cms.InputTag("mix","MergedCaloTruth"), PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), - assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.1) + assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + enFracCut = cms.double(0.), + ptCut = cms.double(0.) +) + +hltPFTesterECALWithCut1 = cms.EDProducer("PFTester", + PFCand = cms.InputTag("hltParticleFlowTmp"), + PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + SimCluster = cms.InputTag("mix","MergedCaloTruth"), + PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), + PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + enFracCut = cms.double(0.01), + ptCut = cms.double(0.) +) + +hltPFTesterECALWithCut2 = cms.EDProducer("PFTester", + PFCand = cms.InputTag("hltParticleFlowTmp"), + PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + SimCluster = cms.InputTag("mix","MergedCaloTruth"), + PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), + PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + enFracCut = cms.double(0.), + ptCut = cms.double(0.1) +) + +hltPFTesterECALWithCut3 = cms.EDProducer("PFTester", + PFCand = cms.InputTag("hltParticleFlowTmp"), + PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), + CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + SimCluster = cms.InputTag("mix","MergedCaloTruth"), + PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), + PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + enFracCut = cms.double(0.01), + ptCut = cms.double(0.1) ) PFValSeq = cms.Sequence( @@ -39,4 +78,7 @@ +hltPFCpAssocByEnergyScoreProducer +hltPFClusterCaloParticleAssociationProducerECAL +hltPFTesterECAL + +hltPFTesterECALWithCut1 + +hltPFTesterECALWithCut2 + +hltPFTesterECALWithCut3 ) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 71f10dd143735..76386fabd0b3b 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -22,6 +22,9 @@ class dotdict(dict): __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ +def debug(mes): + print('### INFO: ' + mes) + def createDir(adir): if not os.path.exists(adir): os.makedirs(adir) @@ -31,13 +34,12 @@ def createIndexPHP(src, dest): php_file = os.path.join(src, 'index.php') if os.path.exists(php_file): os.system(f'cp {php_file} {dest}') - return adir def checkRootDir(afile, adir): if not afile.Get(adir): raise RuntimeError(f"Directory '{adir}' not found in {afile}") -def rate_errorbar_declutter(eff, err, yaxmin, frac=0.01): +def rate_errorbar_declutter(plotter, eff, err, yaxmin, frac=0.01): """ Filter uncertainties if they lie below the minimum (vertical) axis value. Used to plot the filtered points differently, for instance by displaying only the upper uncertainty. @@ -57,7 +59,7 @@ def rate_errorbar_declutter(eff, err, yaxmin, frac=0.01): filt_limit_zero = eff_filt-err_filt/2 < 0. err_hi = np.where(filt_limit_one, 0., err_filt) err_lo = np.where(filt_limit_zero, 0., err_filt) - return eff_filt, (err_lo/2,err_hi/2), up_error, transform + return eff_filt, (err_lo,err_hi), up_error, transform def define_bins(h): """ @@ -159,7 +161,7 @@ def limits_with_margin(self, mdValues, mdErrors, logY=False, logX=False): def save(self, name): for ext in self.extensions: - print(" ### INFO: Saving " + name + '.' + ext) + debug('Saving ' + name + '.' + ext) plt.savefig(name + '.' + ext) plt.close() @@ -170,6 +172,7 @@ def plotProject(h, props, rebin_edges, outname): colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + valuesList, errorsList = [], [] plotter = Plotter(args.sample_label, grid_color=None) for ibin, (low, high) in enumerate(zip(rebin_edges[:-1],rebin_edges[1:])): hproj = h.ProjectionY(h.GetName() + "_proj" + str(ibin), @@ -178,7 +181,9 @@ def plotProject(h, props, rebin_edges, outname): nbins, bin_edges, bin_centers, bin_widths = define_bins(hproj) values, errors = histo_values_errors(hproj) errors /= 2 - + valuesList.append(values) + errorsList.append(errors) + line = plotter.ax.stairs(values, bin_edges, linewidth=2, baseline=None, color=next(colors_iter)) label = f"{low} < {props['var']} < {high} {props['unit']}" @@ -186,7 +191,8 @@ def plotProject(h, props, rebin_edges, outname): color=line.get_edgecolor(), fmt='s', label=label, **errorbar_kwargs) - # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') plotter.ax.grid(color='grey', axis='x') plt.tight_layout() @@ -204,6 +210,19 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): plotter = Plotter(args.sample_label, grid_color=None) for sub in subdirs: root_hist = cached_histos[f"{sub}/{name}"] + + if props['rebin'] is not None: + root_hist = root_hist.Clone(f"{name}" + "_clone") + root_hist.SetDirectory(0) # detach from file + + if isinstance(props['rebin'], (int, float)): + root_hist = root_hist.Rebin(int(props['rebin']), f"{name}" + "_rebin") + elif hasattr(props['rebin'], '__iter__'): + bin_edges_c = array.array('d', props['rebin']) + root_hist = root_hist.Rebin(len(bin_edges_c) - 1, f"{name}" + "_rebin", bin_edges_c) + else: + raise ValueError(f"Unknown type for rebin: {type(props['rebin'])}") + nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) values, errors = histo_values_errors(root_hist) errors /= 2 @@ -211,12 +230,14 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): line = plotter.ax.stairs(values, bin_edges, linewidth=2, baseline=None, color=next(colors_iter)) - sublabel = re.sub(pattern, replacement, sub) - plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, + sublabel = re.sub(pattern, replacement, sub) + if sublabel == 'score = 1.1': sublabel = 'score = 1.0' + eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) - # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.limits(y=(0,1.1)) plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') plotter.ax.grid(color='grey', axis='x') plt.tight_layout() @@ -274,10 +295,9 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): plt.tight_layout() plotter.save( os.path.join(outdir, props['name']) ) -def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix=''): +def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, suffix=''): """ Plots 1D distributions. - The `avars` variables is a dictionary whose values are (xlabel, ylabel, rebin). """ plotter = Plotter(args.sample_label, grid_color=None) ax2 = plotter.ax.twinx() @@ -285,18 +305,38 @@ def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix='' valuesList, errorsList = [], [] colors_iter = iter(('black', 'blue')) - for avar in vars1d: - name, (xlabel, ylabel, rebin, logy, doNormalize, leglabel) = avar - if doNormalize: - ylabel = '[a.u.]' + + histo_names = [vars1d['den'], vars1d['num'], vars1d['ratio']] + leg_names = [vars1d['legden'], vars1d['legnum'], ''] + rebin = vars1d['rebin'] + doNormalize = vars1d['normalize'] + logy = vars1d['logy'] + xlabel = vars1d['xtitle'] + ylabel = vars1d['ytitle'] + + for name, leglabel in zip(histo_names, leg_names): root_hist = cached_histos[name] + + if rebin is not None: + root_hist = root_hist.Clone(f"{root_hist.GetName()}_clone") + root_hist.SetDirectory(0) # detach from file + + if isinstance(rebin, (int, float)): + root_hist = root_hist.Rebin(int(rebin), f"{root_hist.GetName()}_rebin") + elif hasattr(rebin, '__iter__'): + bin_edges_c = array.array('d', rebin) + root_hist = root_hist.Rebin(len(bin_edges_c) - 1, f"{root_hist.GetName()}_rebin", bin_edges_c) + else: + raise ValueError(f"Unknown type for rebin: {type(rebin)}") + nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) values, errors = histo_values_errors(root_hist) errors /= 2 # normalization - doNormalize = False + if doNormalize: + ylabel = '[a.u.]' if 'Eff' not in name and doNormalize: errors /= sum(values) values /= sum(values) @@ -311,13 +351,11 @@ def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix='' elif 'Merge' in name: axis_name = 'Merge Rate' ax2.set_ylabel(axis_name, color=eff_color) - - # ax2.set_yscale('log') ax2.set_ylim(-0.05, 1.15) - # eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(eff_values, eff_errors, axmin) - ax2.stairs(values, bin_edges, linewidth=2, baseline=None, color=eff_color) - ax2.errorbar(bin_centers, values, xerr=None, yerr=errors, + eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) + ax2.stairs(eff_filt, bin_edges, linewidth=2, baseline=None, color=eff_color) + ax2.errorbar(bin_centers, eff_filt, xerr=None, yerr=[err_filt_lo,err_filt_hi], color=eff_color, fmt='s', label=leglabel, **errorbar_kwargs) else: @@ -346,7 +384,7 @@ def plotEffComp1D(cached_histos, vars1d, outdir, text, top_text=False, suffix='' plotter.ax.grid(color=eff_color, axis='x') plt.tight_layout() - plotter.save( os.path.join(outdir, name) ) + plotter.save( os.path.join(outdir, title) ) def plot2D(h, props, outname): """ @@ -415,7 +453,9 @@ def __call__(self, parser, namespace, values, option_string=None): full_command = 'python3 Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py --odir -l TTbar -f ' parser = argparse.ArgumentParser(description='Make HLT PF validation plots. \nExample command:\n' + full_command) parser.add_argument('-o', '--odir', default="HLTPFValidationPlots", help='Path to the output directory.') - parser.add_argument('-l', '--sample_label', default="QCD (200 PU)", help='Sample label for plotting.') + parser.add_argument('-l', '--sample_label', default="", help='Sample label for plotting.') + parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') + parser.add_argument('--PtCut', default=0.01, help='Cut on the sim cluster energy fraction.') mutual_excl2 = parser.add_mutually_exclusive_group(required=True) mutual_excl2.add_argument('-f', '--file', help='Paths to the DQM ROOT file.') @@ -433,14 +473,15 @@ def __call__(self, parser, namespace, values, option_string=None): markers = ('o', 's', 'd') errorbar_kwargs = dict(capsize=3, elinewidth=0.8, capthick=2, linewidth=2, linestyle='') - nEventsLabel = '# Events' + nSimClustersLabel = '# SimClusters' + nPFClustersLabel = '# PFClusters' effLabel = 'Efficiency' - dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation" + dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) - print("### INFO: Start caching histograms...") + debug('Start caching histograms...') subdirs = [] cached_histos = {} directory = afile.GetDirectory(dqm_dir) @@ -455,7 +496,7 @@ def __call__(self, parser, namespace, values, option_string=None): else: name = key.GetName() cached_histos[name] = key.ReadObj() - print("### INFO: ...done.") + debug(' ...done.') for subdir in subdirs: checkRootDir(afile, f"{dqm_dir}/{subdir}") @@ -463,63 +504,88 @@ def __call__(self, parser, namespace, values, option_string=None): createIndexPHP(src=args.odir, dest=f'{args.odir}/{subdir}') for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')): - vars1D = { + varsDict = { # Cluster efficiency - f'SimClusters{suf}En' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersEn' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Eff_vs_En{name}' : ('Energy [GeV]', nEventsLabel, None, True, False, None), - f'SimClusters{suf}Pt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersPt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Eff_vs_Pt{name}' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, None), - f'SimClusters{suf}Eta' : (r'$\eta$', nEventsLabel, None, False, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersEta' : (r'$\eta$', nEventsLabel, None, False, False, 'Reco'), - f'{subdir}/Eff_vs_Eta{name}' : (r'$\eta$', nEventsLabel, None, False, False, None), - f'SimClusters{suf}Phi' : (r'$\phi$', nEventsLabel, None, False, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersPhi' : (r'$\phi$', nEventsLabel, None, False, False, 'Reco'), - f'{subdir}/Eff_vs_Phi{name}' : (r'$\phi$', nEventsLabel, None, False, False, None), - f'SimClusters{suf}Mult' : ('Multiplicity', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/SimClustersMatchedRecoClustersMult' : ('Multiplicity', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Eff_vs_Mult{name}' : ('Multiplicity', nEventsLabel, None, True, False, None), + f'{subdir}/Eff_vs_En{name}': dict(ratio=f'{subdir}/Eff_vs_En{name}', + den=f'SimClusters{suf}En', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEn', legnum='Matched SimClusters', + xtitle='Energy from SimTrack [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_EnHits{name}': dict(ratio=f'{subdir}/Eff_vs_EnHits{name}', + den=f'SimClusters{suf}EnHits', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEnHits', legnum='Matched SimClusters', + xtitle='Energy from hits [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_EnFrac{name}': dict(ratio=f'{subdir}/Eff_vs_EnFrac{name}', + den=f'SimClusters{suf}EnFrac', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEnFrac', legnum='Matched SimClusters', + xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_Pt{name}': dict(ratio=f'{subdir}/Eff_vs_Pt{name}', + den=f'SimClusters{suf}Pt', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersPt', legnum='Matched SimClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_Eta{name}': dict(ratio=f'{subdir}/Eff_vs_Eta{name}', + den=f'SimClusters{suf}Eta', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEta', legnum='Matched SimClusters', + xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Eff_vs_Phi{name}': dict(ratio=f'{subdir}/Eff_vs_Phi{name}', + den=f'SimClusters{suf}Phi', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersPhi', legnum='Matched SimClusters', + xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), # Cluster fake rate - f'RecoClusters{suf}En' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/RecoClustersMatchedSimClustersEn' : ('Energy [GeV]', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Fake_vs_En{name}' : ('Energy [GeV]', nEventsLabel, None, True, False, None), - f'RecoClusters{suf}Pt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/RecoClustersMatchedSimClustersPt' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Fake_vs_Pt{name}' : (r'$p_{T}$ [GeV]', nEventsLabel, None, True, False, None), - f'RecoClusters{suf}Eta' : (r'$\eta$', nEventsLabel, None, False, False, 'Sim'), - f'{subdir}/RecoClustersMatchedSimClustersEta' : (r'$\eta$', nEventsLabel, None, False, False, 'Reco'), - f'{subdir}/Fake_vs_Eta{name}' : (r'$\eta$', nEventsLabel, None, False, False, None), - f'RecoClusters{suf}Phi' : (r'$\phi$', nEventsLabel, None, False, False, 'Sim'), - f'{subdir}/RecoClustersMatchedSimClustersPhi' : (r'$\phi$', nEventsLabel, None, False, False, 'Reco'), - f'{subdir}/Fake_vs_Phi{name}' : (r'$\phi$', nEventsLabel, None, False, False, None), - f'RecoClusters{suf}Mult' : ('Multiplicity', nEventsLabel, None, True, False, 'Sim'), - f'{subdir}/RecoClustersMatchedSimClustersMult' : ('Multiplicity', nEventsLabel, None, True, False, 'Reco'), - f'{subdir}/Fake_vs_Mult{name}' : ('Multiplicity', nEventsLabel, None, True, False, None), + f'{subdir}/Fake_vs_En{name}': dict(ratio=f'{subdir}/Fake_vs_En{name}', + den=f'RecoClusters{suf}En', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersEn', legnum='Matched RecoClusters', + xtitle='Energy from SimTrack [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Fake_vs_Pt{name}': dict(ratio=f'{subdir}/Fake_vs_Pt{name}', + den=f'RecoClusters{suf}Pt', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersPt', legnum='Matched RecoClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Fake_vs_Eta{name}': dict(ratio=f'{subdir}/Fake_vs_Eta{name}', + den=f'RecoClusters{suf}Eta', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersEta', legnum='Matched RecoClusters', + xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Fake_vs_Phi{name}': dict(ratio=f'{subdir}/Fake_vs_Phi{name}', + den=f'RecoClusters{suf}Phi', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersPhi', legnum='Matched RecoClusters', + xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Fake_vs_Mult{name}': dict(ratio=f'{subdir}/Fake_vs_Mult{name}', + den=f'RecoClusters{suf}Mult', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), } # Compare pairs of variables - it = iter(vars1D.items()) - for var in it: - avars = (var, next(it), next(it)) # reco, sim and efficiency for a given variable - plotEffComp1D(cached_histos, vars1d=avars, outdir=args.odir, text='', suffix=f'') + for title, props in varsDict.items(): + plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') - titles = {'response': r"$p_{T}^{Reco}/p_{T}^{Sim}$", 'av_response': r"$$", 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", 'eff': 'Efficiency'} + titles = {'response': r"$p_{T}^{Reco}/p_{T}^{Sim}$", + 'av_response': r"$$", + 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", + 'eff': 'Efficiency'} varsOverlay = { - "ResponsePt_Mean" : dict(ytitle=titles['response'], xtitle=r'$p_{T} [GeV]$', logy=False), - "ResponseEta_Mean" : dict(ytitle=titles['response'], xtitle=r'$\eta$', logy=False), - "ResponsePhi_Mean" : dict(ytitle=titles['response'], xtitle=r'$\phi$', logy=False), - "ResponseMult_Mean" : dict(ytitle=titles['response'], xtitle='Multiplicity', logy=False), - "Eff_vs_Pt_Reconstructable" : dict(ytitle=titles['eff'], xtitle='$p_{T} [GeV]$', logy=False), - "Eff_vs_Eta_Reconstructable" : dict(ytitle=titles['eff'], xtitle=r'$\eta$', logy=False), - "Eff_vs_Phi_Reconstructable" : dict(ytitle=titles['eff'], xtitle=r'$\phi$', logy=False), - "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], xtitle='Multiplicity', logy=False), + "ResponseEn_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Energy from SimTrack [GeV]', logy=False), + "ResponseEnHits_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Energy from hits [GeV]', logy=False), + "ResponseEnFrac_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Energy Fraction', logy=False), + "ResponsePt_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$p_{T} [GeV]$', logy=False), + "ResponseEta_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\eta$', logy=False), + "ResponsePhi_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\phi$', logy=False), + "ResponseMult_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Multiplicity', logy=False), + "Eff_vs_En_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), + "Eff_vs_EnHits_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), + "Eff_vs_EnFrac_Reconstructable" : dict(ytitle=titles['eff'], rebin=6, xtitle='Energy Fraction', logy=False), + "Eff_vs_Pt_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), + "Eff_vs_Eta" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), + "Eff_vs_Eta_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), + "Eff_vs_Phi_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), + "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), } for name, props in varsOverlay.items(): plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) varsResponse = { + ("ResponseEn_Sigma", "ResponseEn_Mean") : dict(name='ResolutionEn', ytitle=titles['resolution'], xtitle=r'$E [GeV]$', rebin=4, logy=False), + ("ResponseEnHits_Sigma", "ResponseEnHits_Mean") : dict(name='ResolutionEnHits', ytitle=titles['resolution'], xtitle=r'$E_{hits} [GeV]$', rebin=4, logy=False), + ("ResponseEnFrac_Sigma", "ResponseEnFrac_Mean") : dict(name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle=r'Energy Fraction', rebin=4, logy=False), ("ResponsePt_Sigma", "ResponsePt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', rebin=4, logy=False), ("ResponseEta_Sigma", "ResponseEta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', rebin=4, logy=False), ("ResponsePhi_Sigma", "ResponsePhi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', rebin=None, logy=False), @@ -527,17 +593,25 @@ def __call__(self, parser, namespace, values, option_string=None): } for (num, den), props in varsResponse.items(): plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir=args.odir) - + vars2D = { - 'ResponsePt' : dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)), - 'ResponseEta' : dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)), - 'ResponsePhi' : dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -2., -1., 0., 1., 2., 3.15)), - 'ResponseMult': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 45., 100., 200.)), + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E_{hist}', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'Energy Fraction', unit='', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)) for subdir in subdirs}, + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -2., -1., 0., 1., 2., 3.15)) for subdir in subdirs}, + **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 45., 100., 200.)) for subdir in subdirs}, + 'SimClustersReconstructableEnFrac_Mult': dict(xtitle='Multiplicity', ytitle='# Clusters', var='Energy Fraction', unit='', logy=True, rebin=(0., 0.005, 0.01, 0.02, 0.03, 1.)), } for name, props in vars2D.items(): plotter = Plotter(args.sample_label, fontsize=15) - for subdir in subdirs: - root_hist = cached_histos[f"{subdir}/{name}"] - plotProject(root_hist, props, rebin_edges=props['rebin'], outname=os.path.join(args.odir, subdir, name + '_Projected')) - plot2D(root_hist, props, outname=os.path.join(args.odir, subdir, name)) + root_hist = cached_histos[f"{name}"] + plotProject(root_hist, props, rebin_edges=props['rebin'], outname=os.path.join(args.odir, name + '_Projected')) + + xtitle, ytitle, var = props['xtitle'], props['ytitle'], props['var'] + props['xtitle'] = var + props['ytitle'] = xtitle + # props['var'] = ytitle + plot2D(root_hist, props, outname=os.path.join(args.odir, name)) From 44fdc92e9355abefdbc3e4106209d81adbd7ce6e Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 29 Oct 2025 16:17:15 +0100 Subject: [PATCH 05/64] Correct simHitCollections to only use ECAL hits --- .../MixingModule/python/caloTruthProducer_cfi.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py index f3c0a2aafd422..fad51271eee96 100644 --- a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py +++ b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py @@ -15,17 +15,11 @@ maximumPreviousBunchCrossing = cms.uint32(0), maximumSubsequentBunchCrossing = cms.uint32(0), simHitCollections = cms.PSet( - hgc = cms.VInputTag( - cms.InputTag('g4SimHits','HGCHitsEE'), - cms.InputTag('g4SimHits','HGCHitsHEfront'), - cms.InputTag('g4SimHits','HGCHitsHEback') - ), -# hcal = cms.VInputTag(cms.InputTag('g4SimHits','HcalHits')), -# ecal = cms.VInputTag( -# cms.InputTag('g4SimHits','EcalHitsEE'), -# cms.InputTag('g4SimHits','EcalHitsEB'), -# cms.InputTag('g4SimHits','EcalHitsES') -# ) + hgc = cms.VInputTag(), +# hcal = cms.VInputTag(cms.InputTag('g4SimHits','HcalHits')), + ecal = cms.VInputTag( + cms.InputTag('g4SimHits','EcalHitsEB'), + ) ), simTrackCollection = cms.InputTag('g4SimHits'), simVertexCollection = cms.InputTag('g4SimHits'), From e38908917891dae6e1b1f30fee1d310d95e20ea2 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Mon, 3 Nov 2025 13:58:57 +0100 Subject: [PATCH 06/64] Enable LogPrint for SC to PC matching --- .../RecoParticleFlow/plugins/PFTester.cc | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 8c5cefa949cf9..8eb2ec1960a6e 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -21,6 +21,10 @@ #include "FWCore/MessageLogger/interface/MessageLogger.h" #include +#include "Geometry/CaloGeometry/interface/CaloGeometry.h" +#include "Geometry/Records/interface/CaloGeometryRecord.h" +#include "DataFormats/GeometryVector/interface/GlobalPoint.h" + class PFTester : public DQMEDAnalyzer { public: explicit PFTester(const edm::ParameterSet&); @@ -30,6 +34,7 @@ class PFTester : public DQMEDAnalyzer { void analyze(const edm::Event&, const edm::EventSetup&) override; std::string doubleToString(double x) const; + edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; edm::EDGetTokenT PFClusterToken_; edm::EDGetTokenT CaloParticleToken_; @@ -165,7 +170,8 @@ class PFTester : public DQMEDAnalyzer { }; PFTester::PFTester(const edm::ParameterSet& iConfig) - : PFCandToken_(consumes(iConfig.getParameter("PFCand"))), + : geometry_token_(esConsumes()), + PFCandToken_(consumes(iConfig.getParameter("PFCand"))), PFClusterToken_(consumes(iConfig.getParameter("PFCluster"))), CaloParticleToken_(consumes(iConfig.getParameter("CaloParticle"))), SimClusterToken_(consumes(iConfig.getParameter("SimCluster"))), @@ -395,7 +401,7 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev } } -void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { +void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { // -------------------------------------------------------------------- // ---------------- PF Candidates ------------------------------------- // -------------------------------------------------------------------- @@ -582,6 +588,32 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { std::vector wasNotFilled(nAssocScoreThresholds_, true); for (const auto& recoPair : simToRecoMatched) { const auto recoPairIdx = recoPair.first.index(); + + #ifdef debug + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + + auto ev = simClusters[simId].g4Tracks()[0].eventId().event(); + auto bx = simClusters[simId].g4Tracks()[0].eventId().bunchCrossing(); + edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx << ", en=" << energySumSimHits << ", hits="; + const auto& hits_fractions = simClusters[simId].hits_and_fractions(); + const auto& hits_energies = simClusters[simId].hits_and_energies(); + + auto itF = hits_fractions.begin(); + auto itE = hits_energies.begin(); + for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { + DetId id(itF->first); + const GlobalPoint pos = caloGeom.getPosition(id); + edm::LogPrint("PFTester") << " DetId=" << itF->first << ", eta=" << pos.eta() << ", phi=" << pos.phi() + << ", en=" << itE->second << ", fr=" << itF->second; + } + edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first << ", score: " << recoPair.second.second << ", hits="; + for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { + DetId id(hit_energy.recHitRef()->detId()); + const GlobalPoint pos = caloGeom.getPosition(id); + edm:: LogPrint("PFTester") << " DetId=" << hit_energy.recHitRef()->detId() << ", eta=" << pos.eta() << ", phi=" << pos.phi() + << ", en=" << hit_energy.recHitRef()->energy() << ", fr=" << hit_energy.fraction(); + } + #endif for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; @@ -604,6 +636,9 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup&) { if (score > thresh) continue; + // cut on shared energy fraction + // if (shared_energy_frac < thresh) + // continue; // numerator histograms must be filled only once per sim cluster // they are filled inside the recoPair loop to enable a different denominator per threshold From 62fbf8760f1f0b3dd531995858d36677130767d6 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Mon, 3 Nov 2025 17:02:49 +0100 Subject: [PATCH 07/64] Fix normalization for shared energy fraction and re-arrange CpToPC associators --- .../RecoParticleFlow/plugins/PFTester.cc | 188 +++++++++++------- .../python/hltPFValidation_cfi.py | 4 + 2 files changed, 122 insertions(+), 70 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 8eb2ec1960a6e..04f6a6c0db1a3 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -36,6 +36,7 @@ class PFTester : public DQMEDAnalyzer { edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; + edm::EDGetTokenT PFRechitToken_; edm::EDGetTokenT PFClusterToken_; edm::EDGetTokenT CaloParticleToken_; edm::EDGetTokenT SimClusterToken_; @@ -67,6 +68,7 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_CaloParticleToSimClusterEnergyFraction_; MonitorElement* h_CaloParticleToSimHitsEnergyFraction_; + MonitorElement* h2d_CPToPC_simToRecoShEnF_Score_; MonitorElement* h_PFClusterE_; MonitorElement* h_PFClusterEta_; @@ -172,6 +174,7 @@ class PFTester : public DQMEDAnalyzer { PFTester::PFTester(const edm::ParameterSet& iConfig) : geometry_token_(esConsumes()), PFCandToken_(consumes(iConfig.getParameter("PFCand"))), + PFRechitToken_(consumes(iConfig.getParameter("PFRechit"))), PFClusterToken_(consumes(iConfig.getParameter("PFCluster"))), CaloParticleToken_(consumes(iConfig.getParameter("CaloParticle"))), SimClusterToken_(consumes(iConfig.getParameter("SimCluster"))), @@ -224,6 +227,7 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev ibook.setCurrentFolder("HLT/ParticleFlow/CaloParticles"); h_CaloParticleToSimClusterEnergyFraction_ = ibook.book1D("CaloParticleToSimClusterEnergyFraction", "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", 100, 0, 2); h_CaloParticleToSimHitsEnergyFraction_ = ibook.book1D("CaloParticleToSimHitsEnergyFraction", "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", 100, 0, 2); + h2d_CPToPC_simToRecoShEnF_Score_ = ibook.book2D("CPToPC_simToRecoShEnF_Score", "CaloParticle #rightarrow PFCluster simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 50, 0, 2, 50, 0, 1); ibook.setCurrentFolder("HLT/ParticleFlow/PFClusters"); h_PFClusterE_ = ibook.book1D("PFClusterE", "PFCluster Energy;E [GeV]", 100, 0, 100); @@ -402,39 +406,18 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev } void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { - // -------------------------------------------------------------------- - // ---------------- PF Candidates ------------------------------------- - // -------------------------------------------------------------------- - - const reco::PFCandidateCollection* pf_candidates; - edm::Handle PFCand; - iEvent.getByToken(PFCandToken_, PFCand); - if (!PFCand.isValid()) { - edm::LogInfo("PFTester") << "Input PFCand collection not found."; - return; - } - - pf_candidates = PFCand.product(); - if (!pf_candidates) { - edm::LogInfo("PFTester") << " Failed to retrieve data required by PFTester.cc"; - return; - } // -------------------------------------------------------------------- - // ---------------- Calo Particles ------------------------------------ + // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- - edm::Handle CaloParticle; - iEvent.getByToken(CaloParticleToken_, CaloParticle); - if (!CaloParticle.isValid()) { - edm::LogInfo("PFTester") << "Input CaloParticle collection not found."; + edm::Handle PFRechit; + iEvent.getByToken(PFRechitToken_, PFRechit); + if (!PFRechit.isValid()) { + edm::LogInfo("PFTester") << "Input PFRechit collection not found."; return; } - auto CaloParticles = *CaloParticle; - - // -------------------------------------------------------------------- - // ---------------- PF Clusters and associators ----------------------- - // -------------------------------------------------------------------- + auto pfRechit = *PFRechit; edm::Handle PFCluster; iEvent.getByToken(PFClusterToken_, PFCluster); @@ -469,29 +452,98 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) auto recoToSimAssoc = *RecoToSimAssociatorCollection; // -------------------------------------------------------------------- - // ----- Calo Particle plots ------------ + // ---------------- Calo Particles and associators -------------------- + // -------------------------------------------------------------------- + + edm::Handle CaloParticle; + iEvent.getByToken(CaloParticleToken_, CaloParticle); + if (!CaloParticle.isValid()) { + edm::LogInfo("PFTester") << "Input CaloParticle collection not found."; + return; + } + auto caloParticles = *CaloParticle; + + edm::Handle> CpToRecoAssociatorCollection; + iEvent.getByToken(CpToRecoAssociatorToken_, CpToRecoAssociatorCollection); + if (!CpToRecoAssociatorCollection.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterCaloParticleAssociator SimToReco collection not found."; + return; + } + auto cpToRecoAssoc = *CpToRecoAssociatorCollection; + + edm::Handle> RecoToCpAssociatorCollection; + iEvent.getByToken(RecoToCpAssociatorToken_, RecoToCpAssociatorCollection); + if (!RecoToCpAssociatorCollection.isValid()) { + edm::LogInfo("PFTester") << "Input PFClusterCaloParticleAssociator RecoToSim collection not found."; + return; + } + auto recoToCpAssoc = *RecoToCpAssociatorCollection; + + // -------------------------------------------------------------------- + // ----- Calo Particles plots ----------------------------------------- // -------------------------------------------------------------------- - uint nSimClustersFromCPs = 0; + std::unordered_map simClusterToCPEnergyMap; - for (unsigned int cpId = 0; cpId < CaloParticles.size(); ++cpId) { + for (unsigned int cpId = 0; cpId < caloParticles.size(); ++cpId) { + + // Fill map: for each simCluster, the energy of the caloParticle computed as the sum of all simClusters arising from it double energySumSimClusters = 0; double energySumSimHits = 0; - nSimClustersFromCPs += CaloParticles[cpId].simClusters().size(); - for (const auto& scRef : CaloParticles[cpId].simClusters()) { + double energyFracSumSimHits = 0; + + for (const auto& scRef : caloParticles[cpId].simClusters()) { auto const& sc = *(scRef); + // Compute energy of caloParticle as sum of simClusters energies (from SimTrack energy) energySumSimClusters += sc.energy(); + // Compute energy of caloParticle as sum of all hits from all simClusters for (auto hit_energy : sc.hits_and_energies()) { energySumSimHits += hit_energy.second; + // simClusterToCPEnergyMap[scRef.key()] += hit_energy.second; } + // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters + for (auto hit_fraction : sc.hits_and_fractions()) { + DetId id(hit_fraction.first); + auto rechitIt = std::find_if(pfRechit.begin(), pfRechit.end(), + [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + if (rechitIt == pfRechit.end()) { + continue; + } else { + energyFracSumSimHits += rechitIt->energy() * hit_fraction.second; + } + } + } + for (const auto& scRef : caloParticles[cpId].simClusters()) { simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; } #ifdef debug - LogDebug("PFTester") << " CaloParticles[" << cpId << "].energy()=" << CaloParticles[cpId].energy() + edm::LogPrint("PFTester") << " caloParticle [" << cpId << "]: energy=" << caloParticles[cpId].energy() << ", energySumSimClusters=" << energySumSimClusters - << ", energySumSimHits=" << energySumSimHits << std::endl; + << ", energySumSimHits=" << energySumSimHits + << ", energyFracSumSimHits=" << energyFracSumSimHits << std::endl; #endif - h_CaloParticleToSimClusterEnergyFraction_->Fill(energySumSimClusters/CaloParticles[cpId].energy()); - h_CaloParticleToSimHitsEnergyFraction_->Fill(energySumSimHits/CaloParticles[cpId].energy()); + + h_CaloParticleToSimClusterEnergyFraction_->Fill(energySumSimClusters/caloParticles[cpId].energy()); + h_CaloParticleToSimHitsEnergyFraction_->Fill(energySumSimHits/caloParticles[cpId].energy()); + + // SimToReco association for caloParticles + const edm::Ref caloParticleRef(CaloParticle, cpId); + const auto& cpToRecoIt = cpToRecoAssoc.find(caloParticleRef); + if (cpToRecoIt == cpToRecoAssoc.end()) + continue; + const auto& cpToRecoMatched = cpToRecoIt->val; + if (cpToRecoMatched.empty()) + continue; + + for (const auto& recoPair : cpToRecoMatched) { + const auto recoPairIdx = recoPair.first.index(); + #ifdef debug + edm::LogPrint("PFTester") << " caloParticle [" << cpId << "] matched to RecoCluster [" << recoPair.first.index() + << "] with shared energy: " << recoPair.second.first + << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", score: " << recoPair.second.second << std::endl; + #endif + // h2d_CPToPC_simToRecoShEnF_Score_->Fill(recoPair.second.first, recoPair.second.second / energyFracSumSimHits); + } } // -------------------------------------------------------------------- @@ -509,6 +561,18 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simClusters[simId].energy()); + double energyFracSumSimHits = 0; + for (auto hit_energy : simClusters[simId].hits_and_fractions()) { + DetId id(hit_energy.first); + auto rechitIt = std::find_if(pfRechit.begin(), pfRechit.end(), + [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + if (rechitIt == pfRechit.end()) { + continue; + } else { + energyFracSumSimHits += rechitIt->energy() * hit_energy.second; + } + } + // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; if (SimClusterToCPEnergyFraction < enFracCut_) @@ -606,7 +670,10 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) edm::LogPrint("PFTester") << " DetId=" << itF->first << ", eta=" << pos.eta() << ", phi=" << pos.phi() << ", en=" << itE->second << ", fr=" << itF->second; } - edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first << ", score: " << recoPair.second.second << ", hits="; + edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" + << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first + << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", score: " << recoPair.second.second << ", hits="; for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { DetId id(hit_energy.recHitRef()->detId()); const GlobalPoint pos = caloGeom.getPosition(id); @@ -620,7 +687,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) auto score = recoPair.second.second; auto shared_energy = recoPair.second.first; - auto shared_energy_frac = shared_energy / energySumSimHits; + auto shared_energy_frac = shared_energy / energyFracSumSimHits; h_simToRecoScore_->Fill(score); h_simToRecoShEnF_->Fill(shared_energy_frac); @@ -714,7 +781,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) #ifdef debug for (const auto& recoPair : simToRecoMatched) { - LogDebug("PFTester") << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() + edm::LogPrint("PFTester") << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() << " shared energy = " << recoPair.second.first << " score = " << recoPair.second.second << std::endl; } @@ -778,7 +845,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) const auto simPairIdx = simPair.first.index(); #ifdef debug - LogDebug("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx + edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx << " score = " << simPair.second << std::endl; #endif @@ -962,40 +1029,21 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } // -------------------------------------------------------------------- + // ---------------- PF Candidates ------------------------------------- // -------------------------------------------------------------------- - edm::Handle> RecoToCpAssociatorCollection; - iEvent.getByToken(RecoToCpAssociatorToken_, RecoToCpAssociatorCollection); - if (!RecoToCpAssociatorCollection.isValid()) { - std::cout << "Input PFClusterCpClusterAssociator RecoToSim collection not found." << std::endl; - edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociator RecoToSim collection not found."; - } else { - auto recCpColl = *RecoToCpAssociatorCollection; - // std::cout << "recCpColl size : " << recCpColl.size() << std::endl; - - for (unsigned int cId = 0; cId < recoClusters.size(); ++cId) { - const edm::Ref clusterRef(PFCluster, cId); - const auto& scsIt = recCpColl.find(clusterRef); - if (scsIt == recCpColl.end()) - continue; - // const auto& scs = scsIt->val; - // if (!scs.empty()) { - // for (const auto& scPair : scs) { - // // std::cout << " recCpColl Cluster id " << cId << " : first=" << scPair.first.index() - // // << " second=" << scPair.second << std::endl; - // } - // } - } + const reco::PFCandidateCollection* pf_candidates; + edm::Handle PFCand; + iEvent.getByToken(PFCandToken_, PFCand); + if (!PFCand.isValid()) { + edm::LogInfo("PFTester") << "Input PFCand collection not found."; + return; } - edm::Handle> CpToRecoAssociatorCollection; - iEvent.getByToken(CpToRecoAssociatorToken_, CpToRecoAssociatorCollection); - if (!CpToRecoAssociatorCollection.isValid()) { - std::cout << "Input PFClusterCpClusterAssociator SimToReco collection not found." << std::endl; - edm::LogInfo("PFTester") << "Input PFClusterCpClusterAssociator SimToReco collection not found."; - } else { - auto CpRecColl = *CpToRecoAssociatorCollection; - // std::cout << "CpRecColl size : " << CpRecColl.size() << std::endl; + pf_candidates = PFCand.product(); + if (!pf_candidates) { + edm::LogInfo("PFTester") << " Failed to retrieve data required by PFTester.cc"; + return; } // -------------------------------------------------------------------- diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 92012cf6b0c00..e156fa067a8a4 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -26,6 +26,7 @@ hltPFTesterECAL = cms.EDProducer("PFTester", PFCand = cms.InputTag("hltParticleFlowTmp"), + PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), CaloParticle = cms.InputTag("mix","MergedCaloTruth"), SimCluster = cms.InputTag("mix","MergedCaloTruth"), @@ -38,6 +39,7 @@ hltPFTesterECALWithCut1 = cms.EDProducer("PFTester", PFCand = cms.InputTag("hltParticleFlowTmp"), + PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), CaloParticle = cms.InputTag("mix","MergedCaloTruth"), SimCluster = cms.InputTag("mix","MergedCaloTruth"), @@ -50,6 +52,7 @@ hltPFTesterECALWithCut2 = cms.EDProducer("PFTester", PFCand = cms.InputTag("hltParticleFlowTmp"), + PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), CaloParticle = cms.InputTag("mix","MergedCaloTruth"), SimCluster = cms.InputTag("mix","MergedCaloTruth"), @@ -62,6 +65,7 @@ hltPFTesterECALWithCut3 = cms.EDProducer("PFTester", PFCand = cms.InputTag("hltParticleFlowTmp"), + PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), CaloParticle = cms.InputTag("mix","MergedCaloTruth"), SimCluster = cms.InputTag("mix","MergedCaloTruth"), From 71a7b04360fc06dc48222831ec585f304e6e1f3f Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 5 Nov 2025 13:56:27 +0100 Subject: [PATCH 08/64] Add plots for fake, duplicate CaloParticles --- .../RecoParticleFlow/plugins/PFTester.cc | 202 +++++------ .../python/hltPFPostProcessor_cfi.py | 150 +++++---- .../scripts/makeHLTPFValidationPlots.py | 317 +++++++++++++----- 3 files changed, 422 insertions(+), 247 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 04f6a6c0db1a3..308304e411853 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -66,10 +66,6 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_TrackNumMeasurements_; MonitorElement* h_TrackImpactParameter_; - MonitorElement* h_CaloParticleToSimClusterEnergyFraction_; - MonitorElement* h_CaloParticleToSimHitsEnergyFraction_; - MonitorElement* h2d_CPToPC_simToRecoShEnF_Score_; - MonitorElement* h_PFClusterE_; MonitorElement* h_PFClusterEta_; MonitorElement* h_PFClusterPhi_; @@ -78,6 +74,13 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_PFClusterType_; MonitorElement* h_PFClusterHitFraction_; MonitorElement* h_PFClusterHitDetId_; + + MonitorElement* h_CaloParticleToSimClusterEnergyFraction_; + MonitorElement* h_CaloParticleToSimHitsEnergyFraction_; + MonitorElement* h_CP_recoToSimScore_; + MonitorElement* h_CP_simToRecoScore_; + MonitorElement* h_CP_simToRecoShEnF_; + MonitorElement* h_CP_simToRecoShEnF_Score_; MonitorElement* h_nPFClusters_; MonitorElement* h_nSimClusters_; @@ -168,7 +171,8 @@ class PFTester : public DQMEDAnalyzer { U2Map h2d_recoClustersReconstructable_; VU2Map h2d_recoClustersMatchedSimClusters_; - VU2Map h2d_response_; + VU2Map h2d_responsePt_; + VU2Map h2d_responseE_; }; PFTester::PFTester(const edm::ParameterSet& iConfig) @@ -196,67 +200,37 @@ PFTester::PFTester(const edm::ParameterSet& iConfig) h_recoClustersMultiMatchedSimClusters_.resize(nAssocScoreThresholds_); h2d_simClustersMatchedRecoClusters_.resize(nAssocScoreThresholds_); h2d_recoClustersMatchedSimClusters_.resize(nAssocScoreThresholds_); - h2d_response_.resize(nAssocScoreThresholds_); + h2d_responsePt_.resize(nAssocScoreThresholds_); + h2d_responseE_.resize(nAssocScoreThresholds_); } void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { - ibook.setCurrentFolder("HLT/ParticleFlow/PFCandidates"); - h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); - h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); - h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); - h_PFCandCharge_ = ibook.book1D("PFCandCharge", "PFCandCharge", 5, -2, 2); - h_PFCandPdgId_ = ibook.book1D("PFCandPdgId", "PFCandPdgId", 44, -22, 22); - h_PFCandType_ = ibook.book1D("PFCandidateType", "PFCandidateType", 10, 0, 10); - - ibook.setCurrentFolder("HLT/ParticleFlow/PFBlocks"); - h_NumElements_ = ibook.book1D("NumElements", "NumElements", 25, 0, 25); - h_NumTrackElements_ = ibook.book1D("NumTrackElements", "NumTrackElements", 5, 0, 5); - h_NumMuonElements_ = ibook.book1D("NumMuonElements", "NumMuonElements", 5, 0, 5); - h_NumPS1Elements_ = ibook.book1D("NumPS1Elements", "NumPS1Elements", 5, 0, 5); - h_NumPS2Elements_ = ibook.book1D("NumPS2Elements", "NumPS2Elements", 5, 0, 5); - h_NumECALElements_ = ibook.book1D("NumECALElements", "NumECALElements", 5, 0, 5); - h_NumHCALElements_ = ibook.book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); - h_NumHGCALElements_ = ibook.book1D("NumHGCALElements", "NumHGCALElements", 5, 0, 5); - ibook.setCurrentFolder("HLT/ParticleFlow/PFTracks"); - h_TrackCharge_ = ibook.book1D("TrackCharge", "TrackCharge", 5, -2, 2); - h_TrackNumPoints_ = ibook.book1D("TrackNumPoints", "TrackNumPoints", 100, 0, 100); - h_TrackNumMeasurements_ = ibook.book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); - h_TrackImpactParameter_ = ibook.book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); - - ibook.setCurrentFolder("HLT/ParticleFlow/CaloParticles"); + ibook.setCurrentFolder("HLT/ParticleFlow/CaloParticles_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_)); h_CaloParticleToSimClusterEnergyFraction_ = ibook.book1D("CaloParticleToSimClusterEnergyFraction", "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", 100, 0, 2); h_CaloParticleToSimHitsEnergyFraction_ = ibook.book1D("CaloParticleToSimHitsEnergyFraction", "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", 100, 0, 2); - h2d_CPToPC_simToRecoShEnF_Score_ = ibook.book2D("CPToPC_simToRecoShEnF_Score", "CaloParticle #rightarrow PFCluster simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 50, 0, 2, 50, 0, 1); - - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusters"); - h_PFClusterE_ = ibook.book1D("PFClusterE", "PFCluster Energy;E [GeV]", 100, 0, 100); - h_PFClusterEta_ = ibook.book1D("PFClusterEta", "PFCluster Eta;#eta", 120, -6, 6); - h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "PFCluster Phi;#phi", 128, -3.2, 3.2); - h_PFClusterDepth_ = ibook.book1D("PFClusterDepth", "PFCluster Depth;Depth", 10, 0, 10); - h_PFClusterNHits_ = ibook.book1D("PFClusterNHits", "PFCluster Number of Hits", 100, 0, 100); - h_PFClusterType_ = ibook.book1D("PFClusterEtaWidth", "PFCluster Eta Width;#sigma_{#eta}", 20, 0, 20); - h_PFClusterHitFraction_ = ibook.book1D("PFClusterHitFraction", "PFCluster Hit Fraction;Fraction", 100, 0.0, 1.1); - h_PFClusterHitDetId_ = - ibook.book1D("PFClusterHitDetId", "PFCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); - + h_CP_recoToSimScore_ = ibook.book1D("CP_recoToSimScore", "recoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); + h_CP_simToRecoScore_ = ibook.book1D("CP_simToRecoScore", "simToRecoScore;CaloParticle Sim #rightarrow Reco score", 51, 0, 1.02); + h_CP_simToRecoShEnF_ = ibook.book1D("CP_simToRecoShEnF", "simToRecoSharedEnergy;CaloParticle Sim #rightarrow Reco shared energy fraction", 51, 0, 1.02); + h_CP_simToRecoShEnF_Score_ = ibook.book2D("CP_simToRecoShEnF_Score", "CaloParticle #rightarrow PFCluster simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 51, 0, 1.02, 51, 0, 1.02); + std::string pfValidFolder = "HLT/ParticleFlow/PFClusterValidation_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_); ibook.setCurrentFolder(pfValidFolder); h_nSimClusters_ = ibook.book1D("nSimClusters", "Number of SimClusters;Number of SimClusters per event", 100, 0, 100); h_nSimClustersPrimary_ = ibook.book1D("nSimClustersPrimary", "Number of Primary SimClusters;Number of Primary SimClusters per event", 100, 0, 100); h_nPFClusters_ = ibook.book1D("nPFClusters", "Number of PFClusters per PFCandidate", 100, 0, 100); - h_recoToSimScore_ = ibook.book1D("recoToSimScore", "recoToSimScore;Reco #rightarrow Sim score", 50, 0, 1); - h_simToRecoScore_ = ibook.book1D("simToRecoScore", "simToRecoScore;Sim #rightarrow Reco score", 50, 0, 1); - h_simToRecoShEnF_ = ibook.book1D("simToRecoShEnF", "simToRecoSharedEnergy;Sim #rightarrow Reco shared energy fraction", 50, 0, 2); - h_simToRecoShEnF_Score_ = ibook.book2D("simToRecoShEnF_Score", "simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 50, 0, 2, 50, 0, 2); - h_simToRecoShEnF_En_ = ibook.book2D("simToRecoShEnF_En", "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy", 50, 0, 2, 100, 0., 100.); - h_simToRecoShEnF_EnHits_ = ibook.book2D("simToRecoShEnF_EnHits", "simToRecoSharedEnergy vs Energy Hits;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", 50, 0, 2, 100, 0., 100.); - h_simToRecoShEnF_EnFrac_ = ibook.book2D("simToRecoShEnF_EnFrac", "simToRecoSharedEnergy vs Energy Fraction;Sim #rightarrow Reco shared energy fraction;EnFrac", 50, 0, 2, 220, 0., 1.1); - h_simToRecoShEnF_Mult_ = ibook.book2D("simToRecoShEnF_Mult", "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", 50, 0, 2, 200, 0., 200.); - h_simToRecoScore_En_ = ibook.book2D("simToRecoScore_En", "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy", 50, 0, 2, 100, 0., 100.); - h_simToRecoScore_EnHits_ = ibook.book2D("simToRecoScore_EnHits", "simToRecoScore vs Energy Hits;Sim #rightarrow Reco score;Energy_{hits}", 50, 0, 2, 100, 0., 100.); - h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", 50, 0, 2, 220, 0., 1.1); - h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 50, 0, 2, 200, 0., 200.); + h_recoToSimScore_ = ibook.book1D("recoToSimScore", "recoToSimScore;Reco #rightarrow Sim score", 51, 0, 1.02); + h_simToRecoScore_ = ibook.book1D("simToRecoScore", "simToRecoScore;Sim #rightarrow Reco score", 51, 0, 1.02); + h_simToRecoShEnF_ = ibook.book1D("simToRecoShEnF", "simToRecoSharedEnergy;Sim #rightarrow Reco shared energy fraction", 51, 0, 1.02); + h_simToRecoShEnF_Score_ = ibook.book2D("simToRecoShEnF_Score", "simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 51, 0, 1.02, 51, 0, 1.02); + h_simToRecoShEnF_En_ = ibook.book2D("simToRecoShEnF_En", "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy", 51, 0, 1.02, 100, 0., 100.); + h_simToRecoShEnF_EnHits_ = ibook.book2D("simToRecoShEnF_EnHits", "simToRecoSharedEnergy vs Energy Hits;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", 51, 0, 1.02, 100, 0., 100.); + h_simToRecoShEnF_EnFrac_ = ibook.book2D("simToRecoShEnF_EnFrac", "simToRecoSharedEnergy vs Energy Fraction;Sim #rightarrow Reco shared energy fraction;EnFrac", 51, 0, 1.02, 220, 0., 1.1); + h_simToRecoShEnF_Mult_ = ibook.book2D("simToRecoShEnF_Mult", "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", 51, 0, 1.02, 200, 0., 200.); + h_simToRecoScore_En_ = ibook.book2D("simToRecoScore_En", "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy", 51, 0, 1.02, 100, 0., 100.); + h_simToRecoScore_EnHits_ = ibook.book2D("simToRecoScore_EnHits", "simToRecoScore vs Energy Hits;Sim #rightarrow Reco score;Energy_{hits}", 51, 0, 1.02, 100, 0., 100.); + h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", 51, 0, 1.02, 220, 0., 1.1); + h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 51, 0, 1.02, 200, 0., 200.); h_SimTrackToSimHitsEnergyFraction_ = ibook.book1D("SimTrackToSimHitsEnergyFraction", "SimTrackToSimHitsEnergyFraction;SimTrack to SimHits energy fraction", 110, 0, 1.1); for (auto& hVar : histoVarsSim) { @@ -293,8 +267,6 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev ibook.setCurrentFolder(pfValidFolder); h_recoClusters_[hVar.first] = ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); - h_recoClustersReconstructable_[hVar.first] = ibook.book1D( - "RecoClustersReconstructable" + hVar.first, "RecoClustersReconstructable;" + hVar.first, nBins, hMin, hMax); for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); @@ -368,15 +340,6 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev nBinsY, hMinY, hMaxY); - h2d_recoClustersReconstructable_[h2dVar.first] = - ibook.book2D("RecoClustersReconstructable" + h2dVar.first, - "RecoClustersReconstructable;" + x_title + ";" + y_title, - nBinsX, - hMinX, - hMaxX, - nBinsY, - hMinY, - hMaxY); for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); @@ -399,10 +362,48 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); - h2d_response_[ithr][hVar.first] = - ibook.book2D("Response" + hVar.first, "Response;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); + h2d_responsePt_[ithr][hVar.first] = + ibook.book2D("ResponsePt_" + hVar.first, "Response p_T;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); + h2d_responseE_[ithr][hVar.first] = + ibook.book2D("ResponseE_" + hVar.first, "Response Energy;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); } } + + ibook.setCurrentFolder("HLT/ParticleFlow/PFCandidates"); + h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); + h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); + h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); + h_PFCandCharge_ = ibook.book1D("PFCandCharge", "PFCandCharge", 5, -2, 2); + h_PFCandPdgId_ = ibook.book1D("PFCandPdgId", "PFCandPdgId", 44, -22, 22); + h_PFCandType_ = ibook.book1D("PFCandidateType", "PFCandidateType", 10, 0, 10); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFBlocks"); + h_NumElements_ = ibook.book1D("NumElements", "NumElements", 25, 0, 25); + h_NumTrackElements_ = ibook.book1D("NumTrackElements", "NumTrackElements", 5, 0, 5); + h_NumMuonElements_ = ibook.book1D("NumMuonElements", "NumMuonElements", 5, 0, 5); + h_NumPS1Elements_ = ibook.book1D("NumPS1Elements", "NumPS1Elements", 5, 0, 5); + h_NumPS2Elements_ = ibook.book1D("NumPS2Elements", "NumPS2Elements", 5, 0, 5); + h_NumECALElements_ = ibook.book1D("NumECALElements", "NumECALElements", 5, 0, 5); + h_NumHCALElements_ = ibook.book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); + h_NumHGCALElements_ = ibook.book1D("NumHGCALElements", "NumHGCALElements", 5, 0, 5); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFTracks"); + h_TrackCharge_ = ibook.book1D("TrackCharge", "TrackCharge", 5, -2, 2); + h_TrackNumPoints_ = ibook.book1D("TrackNumPoints", "TrackNumPoints", 100, 0, 100); + h_TrackNumMeasurements_ = ibook.book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); + h_TrackImpactParameter_ = ibook.book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); + + ibook.setCurrentFolder("HLT/ParticleFlow/PFClusters"); + h_PFClusterE_ = ibook.book1D("PFClusterE", "PFCluster Energy;E [GeV]", 100, 0, 100); + h_PFClusterEta_ = ibook.book1D("PFClusterEta", "PFCluster Eta;#eta", 120, -6, 6); + h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "PFCluster Phi;#phi", 128, -3.2, 3.2); + h_PFClusterDepth_ = ibook.book1D("PFClusterDepth", "PFCluster Depth;Depth", 10, 0, 10); + h_PFClusterNHits_ = ibook.book1D("PFClusterNHits", "PFCluster Number of Hits", 100, 0, 100); + h_PFClusterType_ = ibook.book1D("PFClusterEtaWidth", "PFCluster Eta Width;#sigma_{#eta}", 20, 0, 20); + h_PFClusterHitFraction_ = ibook.book1D("PFClusterHitFraction", "PFCluster Hit Fraction;Fraction", 100, 0.0, 1.1); + h_PFClusterHitDetId_ = + ibook.book1D("PFClusterHitDetId", "PFCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); + } void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { @@ -498,7 +499,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // Compute energy of caloParticle as sum of all hits from all simClusters for (auto hit_energy : sc.hits_and_energies()) { energySumSimHits += hit_energy.second; - // simClusterToCPEnergyMap[scRef.key()] += hit_energy.second; } // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters for (auto hit_fraction : sc.hits_and_fractions()) { @@ -535,14 +535,29 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) continue; for (const auto& recoPair : cpToRecoMatched) { - const auto recoPairIdx = recoPair.first.index(); #ifdef debug edm::LogPrint("PFTester") << " caloParticle [" << cpId << "] matched to RecoCluster [" << recoPair.first.index() << "] with shared energy: " << recoPair.second.first << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits << ", score: " << recoPair.second.second << std::endl; #endif - // h2d_CPToPC_simToRecoShEnF_Score_->Fill(recoPair.second.first, recoPair.second.second / energyFracSumSimHits); + h_CP_simToRecoScore_->Fill(recoPair.second.second); + h_CP_simToRecoShEnF_->Fill(recoPair.second.first / energyFracSumSimHits); + h_CP_simToRecoShEnF_Score_->Fill(recoPair.second.first / energyFracSumSimHits, recoPair.second.second); + } + } + + // RecoToSim association for caloParticles + for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { + const edm::Ref recoClusterRef(PFCluster, recoId); + const auto& recoToCpIt = recoToCpAssoc.find(recoClusterRef); + if (recoToCpIt == recoToCpAssoc.end()) + continue; + const auto& recoToCpMatched = recoToCpIt->val; + if (recoToCpMatched.empty()) + continue; + for (const auto& cpPair : recoToCpMatched) { + h_CP_recoToSimScore_->Fill(cpPair.second); } } @@ -824,22 +839,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) if (recoToSimMatched.empty()) continue; - h_recoClustersReconstructable_["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersReconstructable_["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersReconstructable_["PtLow"]->Fill(recoClusters[recoId].pt()); - h_recoClustersReconstructable_["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClustersReconstructable_["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClustersReconstructable_["Mult"]->Fill(recoClusters[recoId].size()); - - h2d_recoClustersReconstructable_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); - h2d_recoClustersReconstructable_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); - h2d_recoClustersReconstructable_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); - h2d_recoClustersReconstructable_["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); - h2d_recoClustersReconstructable_["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); - h2d_recoClustersReconstructable_["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); - h2d_recoClustersReconstructable_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); - h2d_recoClustersReconstructable_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - std::vector wasNotFilled(nAssocScoreThresholds_, true); for (const auto& simPair : recoToSimMatched) { const auto simPairIdx = simPair.first.index(); @@ -1010,20 +1009,35 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } if (fill) { - h2d_response_[ithr]["En"]->Fill(simClusters[simId].energy(), + h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["EnHits"]->Fill(energySumSimHits, + h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, + h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["Pt"]->Fill(simClusters[simId].pt(), + h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, + h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["Phi"]->Fill(simClusters[simId].phi(), + h2d_responsePt_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_response_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), + h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].pt() / simClusters[simId].pt()); + + h2d_responseE_[ithr]["En"]->Fill(simClusters[simId].energy(), + recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["EnHits"]->Fill(energySumSimHits, + recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, + recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), + recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, + recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), + recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), + recoClusters[recoId].energy() / energySumSimHits); } } } diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 15e4db1faa333..69261dbc51dd8 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -9,84 +9,94 @@ subDirs=cms.untracked.vstring("HLT/ParticleFlow/PFClusterValidation*"), efficiency = cms.vstring( *[ item - for thr in _thresholds - for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) - for item in ( - f"'Score{thr}/Eff_vs_EnEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", - f"'Score{thr}/Eff_vs_EnPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", - f"'Score{thr}/Eff_vs_EnMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", - f"'Score{thr}/Eff_vs_EnHitsEta{name}' 'Efficiency vs Hits Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Eta SimClusters{suf}EnHits_Eta", - f"'Score{thr}/Eff_vs_EnHitsPhi{name}' 'Efficiency vs Hits Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Phi SimClusters{suf}EnHits_Phi", - f"'Score{thr}/Eff_vs_EnHitsMult{name}' 'Efficiency vs Hits Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Mult SimClusters{suf}EnHits_Mult", - f"'Score{thr}/Eff_vs_EnFracEta{name}' 'Efficiency vs Energy fraction-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Eta SimClusters{suf}EnFrac_Eta", - f"'Score{thr}/Eff_vs_EnFracPhi{name}' 'Efficiency vs Energy fraction-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Phi SimClusters{suf}EnFrac_Phi", - f"'Score{thr}/Eff_vs_EnFracMult{name}' 'Efficiency vs Energy fraction-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Mult SimClusters{suf}EnFrac_Mult", - f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", - f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", - f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", - f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", - f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", - f"'Score{thr}/Fake_vs_EnEta{name}' 'Fake Rate vs Energy-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClusters{suf}Pt_Eta fake", - f"'Score{thr}/Fake_vs_EnPhi{name}' 'Fake Rate vs Energy-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClusters{suf}Pt_Phi fake", - f"'Score{thr}/Fake_vs_EnMult{name}' 'Fake Rate vs Energy-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClusters{suf}Pt_Mult fake", - f"'Score{thr}/Fake_vs_PtEta{name}' 'Fake Rate vs p_{{T}}-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClusters{suf}Pt_Eta fake", - f"'Score{thr}/Fake_vs_PtPhi{name}' 'Fake Rate vs p_{{T}}-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClusters{suf}Pt_Phi fake", - f"'Score{thr}/Fake_vs_PtMult{name}' 'Fake Rate vs p_{{T}}-Mult {suf}' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClusters{suf}Pt_Mult fake", - f"'Score{thr}/Fake_vs_MultEta{name}' 'Fake Rate vs Mult-#eta {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Eta RecoClusters{suf}Mult_Eta fake", - f"'Score{thr}/Fake_vs_MultPhi{name}' 'Fake Rate vs Mult-#phi {suf}' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClusters{suf}Mult_Phi fake", - ) - ], + for thr in _thresholds + for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) + for item in ( + f"'Score{thr}/Eff_vs_EnEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", + f"'Score{thr}/Eff_vs_EnPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", + f"'Score{thr}/Eff_vs_EnMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", + f"'Score{thr}/Eff_vs_EnHitsEta{name}' 'Efficiency vs Hits Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Eta SimClusters{suf}EnHits_Eta", + f"'Score{thr}/Eff_vs_EnHitsPhi{name}' 'Efficiency vs Hits Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Phi SimClusters{suf}EnHits_Phi", + f"'Score{thr}/Eff_vs_EnHitsMult{name}' 'Efficiency vs Hits Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Mult SimClusters{suf}EnHits_Mult", + f"'Score{thr}/Eff_vs_EnFracEta{name}' 'Efficiency vs Energy fraction-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Eta SimClusters{suf}EnFrac_Eta", + f"'Score{thr}/Eff_vs_EnFracPhi{name}' 'Efficiency vs Energy fraction-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Phi SimClusters{suf}EnFrac_Phi", + f"'Score{thr}/Eff_vs_EnFracMult{name}' 'Efficiency vs Energy fraction-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Mult SimClusters{suf}EnFrac_Mult", + f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", + f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", + f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", + f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", + f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", + ) + ], + *[ item + for thr in _thresholds + for item in ( + f"'Score{thr}/Fake_vs_EnEta' 'Fake Rate vs Energy-#eta' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClustersPt_Eta fake", + f"'Score{thr}/Fake_vs_EnPhi' 'Fake Rate vs Energy-#phi' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClustersPt_Phi fake", + f"'Score{thr}/Fake_vs_EnMult' 'Fake Rate vs Energy-Mult' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClustersPt_Mult fake", + f"'Score{thr}/Fake_vs_PtEta' 'Fake Rate vs p_{{T}}-#eta' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClustersPt_Eta fake", + f"'Score{thr}/Fake_vs_PtPhi' 'Fake Rate vs p_{{T}}-#phi' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClustersPt_Phi fake", + f"'Score{thr}/Fake_vs_PtMult' 'Fake Rate vs p_{{T}}-Mult' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClustersPt_Mult fake", + f"'Score{thr}/Fake_vs_MultEta' 'Fake Rate vs Mult-#eta' Score{thr}/RecoClustersMatchedSimClustersMult_Eta RecoClustersMult_Eta fake", + f"'Score{thr}/Fake_vs_MultPhi' 'Fake Rate vs Mult-#phi' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClustersMult_Phi fake", + ) + ], ), efficiencyProfile = cms.untracked.vstring( # for smoother rebinning *[ item - for thr in _thresholds - for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) - for item in ( - # Efficiency - f"'Score{thr}/Eff_vs_En{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", - f"'Score{thr}/Eff_vs_EnHits{name}' 'Efficiency vs Hits Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits SimClusters{suf}EnHits", - f"'Score{thr}/Eff_vs_EnFrac{name}' 'Efficiency vs Energy fraction {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", - f"'Score{thr}/Eff_vs_Pt{name}' 'Efficiency vs p_{{T}} {suf}' Score{thr}/SimClustersMatchedRecoClustersPt SimClusters{suf}Pt", - f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", - f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", - f"'Score{thr}/Eff_vs_Mult{name}' 'Efficiency vs Multiplicity {suf}' Score{thr}/SimClustersMatchedRecoClustersMult SimClusters{suf}Mult", - # Fake rate - f"'Score{thr}/Fake_vs_En{name}' 'Fake Rate vs Energy {suf}' Score{thr}/RecoClustersMatchedSimClustersEn RecoClusters{suf}En", - f"'Score{thr}/Fake_vs_Pt{name}' 'Fake Rate vs p_{{T}} {suf}' Score{thr}/RecoClustersMatchedSimClustersPt RecoClusters{suf}Pt", - f"'Score{thr}/Fake_vs_Eta{name}' 'Fake Rate vs #eta {suf}' Score{thr}/RecoClustersMatchedSimClustersEta RecoClusters{suf}Eta", - f"'Score{thr}/Fake_vs_Phi{name}' 'Fake Rate vs #phi {suf}' Score{thr}/RecoClustersMatchedSimClustersPhi RecoClusters{suf}Phi", - f"'Score{thr}/Fake_vs_Mult{name}' 'Fake Rate vs Multiplicity {suf}' Score{thr}/RecoClustersMatchedSimClustersMult RecoClusters{suf}Mult", - # Duplicate rate - f"'Score{thr}/Dup_vs_En{name}' 'Dup Rate vs Energy {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersEn RecoClusters{suf}En", - f"'Score{thr}/Dup_vs_Pt{name}' 'Dup Rate vs p_{{T}} {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersPt RecoClusters{suf}Pt", - f"'Score{thr}/Dup_vs_Eta{name}' 'Dup Rate vs #eta {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersEta RecoClusters{suf}Eta", - f"'Score{thr}/Dup_vs_Phi{name}' 'Dup Rate vs #phi {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersPhi RecoClusters{suf}Phi", - f"'Score{thr}/Dup_vs_Mult{name}' 'Dup Rate vs Mult {suf}' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClusters{suf}Mult", - # Merge rate - f"'Score{thr}/Merge_vs_En{name}' 'Merge Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", - f"'Score{thr}/Merge_vs_EnHits{name}' 'Merge Rate vs Hits Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnHits SimClusters{suf}EnHits", - f"'Score{thr}/Merge_vs_EnFrac{name}' 'Merge Rate vs Energy fraction {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", - f"'Score{thr}/Merge_vs_Pt{name}' 'Merge Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", - f"'Score{thr}/Merge_vs_Eta{name}' 'Merge Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", - f"'Score{thr}/Merge_vs_Phi{name}' 'Merge Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", - f"'Score{thr}/Merge_vs_Mult{name}' 'Merge Rate vs Multiplicity {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClusters{suf}Mult", - ) + for thr in _thresholds + for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) + for item in ( + # Efficiency + f"'Score{thr}/Eff_vs_En{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Eff_vs_EnHits{name}' 'Efficiency vs Hits Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits SimClusters{suf}EnHits", + f"'Score{thr}/Eff_vs_EnFrac{name}' 'Efficiency vs Energy fraction {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", + f"'Score{thr}/Eff_vs_Pt{name}' 'Efficiency vs p_{{T}} {suf}' Score{thr}/SimClustersMatchedRecoClustersPt SimClusters{suf}Pt", + f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", + f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", + f"'Score{thr}/Eff_vs_Mult{name}' 'Efficiency vs Multiplicity {suf}' Score{thr}/SimClustersMatchedRecoClustersMult SimClusters{suf}Mult", + # Merge rate + f"'Score{thr}/Merge_vs_En{name}' 'Merge Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Merge_vs_EnHits{name}' 'Merge Rate vs Hits Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnHits SimClusters{suf}EnHits", + f"'Score{thr}/Merge_vs_EnFrac{name}' 'Merge Rate vs Energy fraction {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", + f"'Score{thr}/Merge_vs_Pt{name}' 'Merge Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", + f"'Score{thr}/Merge_vs_Eta{name}' 'Merge Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", + f"'Score{thr}/Merge_vs_Phi{name}' 'Merge Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", + f"'Score{thr}/Merge_vs_Mult{name}' 'Merge Rate vs Multiplicity {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClusters{suf}Mult", + ) + ], + *[ item + for thr in _thresholds + for item in ( + # Fake rate + f"'Score{thr}/Fake_vs_En' 'Fake Rate vs Energy' Score{thr}/RecoClustersMatchedSimClustersEn RecoClustersEn fake", + f"'Score{thr}/Fake_vs_Pt' 'Fake Rate vs p_{{T}}' Score{thr}/RecoClustersMatchedSimClustersPt RecoClustersPt fake", + f"'Score{thr}/Fake_vs_Eta' 'Fake Rate vs #eta' Score{thr}/RecoClustersMatchedSimClustersEta RecoClustersEta fake", + f"'Score{thr}/Fake_vs_Phi' 'Fake Rate vs #phi' Score{thr}/RecoClustersMatchedSimClustersPhi RecoClustersPhi fake", + f"'Score{thr}/Fake_vs_Mult' 'Fake Rate vs Multiplicity' Score{thr}/RecoClustersMatchedSimClustersMult RecoClustersMult fake", + # Duplicate rate + f"'Score{thr}/Dup_vs_En' 'Dup Rate vs Energy' Score{thr}/RecoClustersMultiMatchedSimClustersEn RecoClustersEn", + f"'Score{thr}/Dup_vs_Pt' 'Dup Rate vs p_{{T}}' Score{thr}/RecoClustersMultiMatchedSimClustersPt RecoClustersPt", + f"'Score{thr}/Dup_vs_Eta' 'Dup Rate vs #eta' Score{thr}/RecoClustersMultiMatchedSimClustersEta RecoClustersEta", + f"'Score{thr}/Dup_vs_Phi' 'Dup Rate vs #phi' Score{thr}/RecoClustersMultiMatchedSimClustersPhi RecoClustersPhi", + f"'Score{thr}/Dup_vs_Mult' 'Dup Rate vs Mult' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClustersMult", + ) ], ), resolution = cms.vstring(), resolutionProfile = cms.untracked.vstring( *[ item - for thr in _thresholds - for item in ( - f"'Score{thr}/ResponseEn' 'Response vs Energy' Score{thr}/ResponseEn rms", - f"'Score{thr}/ResponseEnHits' 'Response vs Hits Energy' Score{thr}/ResponseEnHits rms", - f"'Score{thr}/ResponseEnFrac' 'Response vs Energy fraction' Score{thr}/ResponseEnFrac rms", - f"'Score{thr}/ResponsePt' 'Response vs p_{{T}}' Score{thr}/ResponsePt rms", - f"'Score{thr}/ResponseEta' 'Response vs #eta' Score{thr}/ResponseEta rms", - f"'Score{thr}/ResponsePhi' 'Response vs #phi' Score{thr}/ResponsePhi rms", - f"'Score{thr}/ResponseMult' 'Response vs Multiplicity' Score{thr}/ResponseMult rms" - ) - ], + for thr in _thresholds + for item in ( + f"'Score{thr}/ResponseE_En' 'Response vs Energy' Score{thr}/ResponseE_En rms", + f"'Score{thr}/ResponseE_EnHits' 'Response vs Hits Energy' Score{thr}/ResponseE_EnHits rms", + f"'Score{thr}/ResponseE_EnFrac' 'Response vs Energy fraction' Score{thr}/ResponseE_EnFrac rms", + f"'Score{thr}/ResponseE_Pt' 'Response vs p_{{T}}' Score{thr}/ResponseE_Pt rms", + f"'Score{thr}/ResponseE_Eta' 'Response vs #eta' Score{thr}/ResponseE_Eta rms", + f"'Score{thr}/ResponseE_Phi' 'Response vs #phi' Score{thr}/ResponseE_Phi rms", + f"'Score{thr}/ResponseE_Mult' 'Response vs Multiplicity' Score{thr}/ResponseE_Mult rms" + ) + ], ), verbose = cms.untracked.uint32(2), outputFileName = cms.untracked.string("") diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 76386fabd0b3b..52bc5073dcb86 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -225,6 +225,7 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) values, errors = histo_values_errors(root_hist) + if 'Fake' in name: values = np.array([1-i if i != 0 else np.nan for i in values]) errors /= 2 line = plotter.ax.stairs(values, bin_edges, linewidth=2, @@ -232,14 +233,20 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): sublabel = re.sub(pattern, replacement, sub) if sublabel == 'score = 1.1': sublabel = 'score = 1.0' - eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) - plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], + if any(x in name for x in ('Eff', 'Fake', 'Dup', 'Merge')): + eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], + color=line.get_edgecolor(), + fmt='s', label=sublabel, **errorbar_kwargs) + plotter.limits(y=(0,1.1), x=(bin_edges[0], bin_edges[-1])) + else: + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) + plotter.limits(x=(bin_edges[0], bin_edges[-1])) - plotter.limits(y=(0,1.1)) plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') - plotter.ax.grid(color='grey', axis='x') + plotter.ax.grid(color='grey') plt.tight_layout() plotter.save( os.path.join(outdir, name) ) @@ -290,6 +297,7 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): fmt='s', label=sublabel, **errorbar_kwargs) # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + plotter.limits(x=(bin_edges[0], bin_edges[-1])) plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') plotter.ax.grid(color='grey', axis='x') plt.tight_layout() @@ -337,11 +345,13 @@ def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, su # normalization if doNormalize: ylabel = '[a.u.]' - if 'Eff' not in name and doNormalize: + if 'Eff' not in name and doNormalize and sum(values) > 0: errors /= sum(values) values /= sum(values) + if 'Fake' in name: + values = np.array([1-i if i != 0 else np.nan for i in values]) - if any(x in name for x in ('Eff', 'Fake', 'Duplicate', 'Merge')): + if any(x in name for x in ('Eff', 'Fake', 'Dup', 'Merge')): if 'Eff' in name: axis_name = 'Efficiency' elif 'Fake' in name: @@ -400,35 +410,26 @@ def plot2D(h, props, outname): cmap='viridis', shading='auto') plotter.labels(x=props['xtitle'], y=props['ytitle']) - plotter.fig.colorbar(pcm, ax=plotter.ax, label='# Clusters') + plotter.fig.colorbar(pcm, ax=plotter.ax, label=props['var']) plt.tight_layout() plotter.save(outname) -# def plot1D(afile, adir, avars, outdir, text, top_text=False): -# for var, (xlabel, ylabel, rebin, logy, _) in avars.items(): -# plotter = Plotter(args.sample_label) -# root_hist = checkRootFile(afile, f"{adir}/{var}", rebin=rebin) -# nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) -# values, errors = histo_values_errors(root_hist) -# errors /= 2 - -# plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, -# fmt='s', color='black', label=var, **errorbar_kwargs) -# plotter.ax.stairs(values, bin_edges, color='black', linewidth=2, baseline=None) +def plot1D(h, props, outname): -# plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, -# verticalalignment='top', horizontalalignment='left') + plotter = Plotter(args.sample_label, fontsize=15) + nbins, bin_edges, bin_centers, bin_widths = define_bins(h) + values, errors = histo_values_errors(h) + errors /= 2 -# plotter.limits_with_margin(values, errors, logY=logy) -# plotter.labels(x=xlabel, y=ylabel) + plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, + fmt='s', color='black', label=props['var'], **errorbar_kwargs) + plotter.ax.stairs(values, bin_edges, color='black', linewidth=2, baseline=None) -# if top_text: -# plotter.ax.text(0.97, 0.97, root_hist.GetTitle().replace('ET', r'$E_T$'), -# transform=plotter.ax.transAxes, fontsize=fontsize, -# verticalalignment='top', horizontalalignment='right') + plotter.limits_with_margin(values, errors, logY=props['logy']) + plotter.labels(x=props['xtitle'], y=props['ytitle']) -# plt.tight_layout() -# plotter.save( os.path.join(outdir, var) ) + plt.tight_layout() + plotter.save( os.path.join(outname) ) if __name__ == '__main__': @@ -475,13 +476,20 @@ def __call__(self, parser, namespace, values, option_string=None): nSimClustersLabel = '# SimClusters' nPFClustersLabel = '# PFClusters' - effLabel = 'Efficiency' + + titles = {'response': r"$p_{T}^{Reco}/p_{T}^{Sim}$", + 'av_response': r"$$", + 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", + 'eff': 'Efficiency', + 'fake': 'Fake Rate', + 'dup': 'Duplicate Rate', + 'merge': 'Merge Rate'} dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) - debug('Start caching histograms...') + debug('Start caching PFCluster histograms...') subdirs = [] cached_histos = {} directory = afile.GetDirectory(dqm_dir) @@ -530,88 +538,231 @@ def __call__(self, parser, namespace, values, option_string=None): den=f'SimClusters{suf}Phi', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersPhi', legnum='Matched SimClusters', xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - # Cluster fake rate - f'{subdir}/Fake_vs_En{name}': dict(ratio=f'{subdir}/Fake_vs_En{name}', - den=f'RecoClusters{suf}En', legden='RecoClusters', - num=f'{subdir}/RecoClustersMatchedSimClustersEn', legnum='Matched RecoClusters', - xtitle='Energy from SimTrack [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Fake_vs_Pt{name}': dict(ratio=f'{subdir}/Fake_vs_Pt{name}', - den=f'RecoClusters{suf}Pt', legden='RecoClusters', - num=f'{subdir}/RecoClustersMatchedSimClustersPt', legnum='Matched RecoClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Fake_vs_Eta{name}': dict(ratio=f'{subdir}/Fake_vs_Eta{name}', - den=f'RecoClusters{suf}Eta', legden='RecoClusters', - num=f'{subdir}/RecoClustersMatchedSimClustersEta', legnum='Matched RecoClusters', - xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Fake_vs_Phi{name}': dict(ratio=f'{subdir}/Fake_vs_Phi{name}', - den=f'RecoClusters{suf}Phi', legden='RecoClusters', - num=f'{subdir}/RecoClustersMatchedSimClustersPhi', legnum='Matched RecoClusters', - xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Fake_vs_Mult{name}': dict(ratio=f'{subdir}/Fake_vs_Mult{name}', - den=f'RecoClusters{suf}Mult', legden='RecoClusters', - num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', - xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_Mult{name}': dict(ratio=f'{subdir}/Eff_vs_Mult{name}', + den=f'SimClusters{suf}Mult', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersMult', legnum='Matched SimClusters', + xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + # Cluster merge rate (WIP) + f'{subdir}/Merge_vs_En{name}': dict(ratio=f'{subdir}/Merge_vs_En{name}', + den=f'SimClusters{suf}En', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEn', legnum='Multi Matched SimClusters', + xtitle='Energy from SimTrack [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Merge_vs_EnHits{name}': dict(ratio=f'{subdir}/Merge_vs_EnHits{name}', + den=f'SimClusters{suf}EnHits', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnHits', legnum='Multi Matched SimClusters', + xtitle='Energy from hits [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Merge_vs_EnFrac{name}': dict(ratio=f'{subdir}/Merge_vs_EnFrac{name}', + den=f'SimClusters{suf}EnFrac', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnFrac', legnum='Multi Matched SimClusters', + xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Merge_vs_Pt{name}': dict(ratio=f'{subdir}/Merge_vs_Pt{name}', + den=f'SimClusters{suf}Pt', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersPt', legnum='Multi Matched SimClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Merge_vs_Eta{name}': dict(ratio=f'{subdir}/Merge_vs_Eta{name}', + den=f'SimClusters{suf}Eta', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEta', legnum='Multi Matched SimClusters', + xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Merge_vs_Phi{name}': dict(ratio=f'{subdir}/Merge_vs_Phi{name}', + den=f'SimClusters{suf}Phi', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersPhi', legnum='Multi Matched SimClusters', + xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Merge_vs_Mult{name}': dict(ratio=f'{subdir}/Merge_vs_Mult{name}', + den=f'SimClusters{suf}Mult', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersMult', legnum='Multi Matched SimClusters', + xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), } # Compare pairs of variables for title, props in varsDict.items(): plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') - - titles = {'response': r"$p_{T}^{Reco}/p_{T}^{Sim}$", - 'av_response': r"$$", - 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", - 'eff': 'Efficiency'} + + varsDict = { + # Cluster fake rate + f'{subdir}/Fake_vs_En': dict(ratio=f'{subdir}/Fake_vs_En', + den=f'RecoClustersEn', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersEn', legnum='Matched RecoClusters', + xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Fake_vs_Pt': dict(ratio=f'{subdir}/Fake_vs_Pt', + den=f'RecoClustersPt', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersPt', legnum='Matched RecoClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Fake_vs_Eta': dict(ratio=f'{subdir}/Fake_vs_Eta', + den=f'RecoClustersEta', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersEta', legnum='Matched RecoClusters', + xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Fake_vs_Phi': dict(ratio=f'{subdir}/Fake_vs_Phi', + den=f'RecoClustersPhi', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersPhi', legnum='Matched RecoClusters', + xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Fake_vs_Mult': dict(ratio=f'{subdir}/Fake_vs_Mult', + den=f'RecoClustersMult', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + # Cluster duplicate rate (WIP) + f'{subdir}/Dup_vs_En': dict(ratio=f'{subdir}/Dup_vs_En', + den=f'RecoClustersEn', legden='RecoClusters', + num=f'{subdir}/RecoClustersMultiMatchedSimClustersEn', legnum='Multi Matched RecoClusters', + xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Dup_vs_Pt': dict(ratio=f'{subdir}/Dup_vs_Pt', + den=f'RecoClustersPt', legden='RecoClusters', + num=f'{subdir}/RecoClustersMultiMatchedSimClustersPt', legnum='Multi Matched RecoClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Dup_vs_Eta': dict(ratio=f'{subdir}/Dup_vs_Eta', + den=f'RecoClustersEta', legden='RecoClusters', + num=f'{subdir}/RecoClustersMultiMatchedSimClustersEta', legnum='Multi Matched RecoClusters', + xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Dup_vs_Phi': dict(ratio=f'{subdir}/Dup_vs_Phi', + den=f'RecoClustersPhi', legden='RecoClusters', + num=f'{subdir}/RecoClustersMultiMatchedSimClustersPhi', legnum='Multi Matched RecoClusters', + xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Dup_vs_Mult': dict(ratio=f'{subdir}/Dup_vs_Mult', + den=f'RecoClustersMult', legden='RecoClusters', + num=f'{subdir}/RecoClustersMultiMatchedSimClustersMult', legnum='Multi Matched RecoClusters', + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + } + + # Compare pairs of variables + for title, props in varsDict.items(): + plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') varsOverlay = { - "ResponseEn_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Energy from SimTrack [GeV]', logy=False), - "ResponseEnHits_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Energy from hits [GeV]', logy=False), - "ResponseEnFrac_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Energy Fraction', logy=False), - "ResponsePt_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$p_{T} [GeV]$', logy=False), - "ResponseEta_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\eta$', logy=False), - "ResponsePhi_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\phi$', logy=False), - "ResponseMult_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle='Multiplicity', logy=False), + "ResponseE_En_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='Energy from SimTrack [GeV]', logy=False), + "ResponseE_EnHits_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='Energy from hits [GeV]', logy=False), + "ResponseE_EnFrac_Mean" : dict(ytitle=titles['response'], rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), xtitle='Energy Fraction', logy=False), + "ResponseE_Pt_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle=r'$p_{T} [GeV]$', logy=False), + "ResponseE_Eta_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\eta$', logy=False), + "ResponseE_Phi_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\phi$', logy=False), + "ResponseE_Mult_Mean" : dict(ytitle=titles['response'], rebin=4, xtitle='Multiplicity', logy=False), + "Eff_vs_En" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), "Eff_vs_En_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), + "Eff_vs_EnHits" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), "Eff_vs_EnHits_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), + "Eff_vs_EnFrac" : dict(ytitle=titles['eff'], rebin=6, xtitle='Energy Fraction', logy=False), "Eff_vs_EnFrac_Reconstructable" : dict(ytitle=titles['eff'], rebin=6, xtitle='Energy Fraction', logy=False), + "Eff_vs_Pt" : dict(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), "Eff_vs_Pt_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), "Eff_vs_Eta" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), "Eff_vs_Eta_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), + "Eff_vs_Phi" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), "Eff_vs_Phi_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), + "Eff_vs_Mult" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), + "Merge_vs_En" : dict(ytitle=titles['merge'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), + "Merge_vs_EnHits" : dict(ytitle=titles['merge'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), + "Merge_vs_EnFrac" : dict(ytitle=titles['merge'], rebin=6, xtitle='Energy Fraction', logy=False), + "Merge_vs_Pt" : dict(ytitle=titles['merge'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), + "Merge_vs_Eta" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\eta$', logy=False), + "Merge_vs_Phi" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\phi$', logy=False), + "Merge_vs_Mult" : dict(ytitle=titles['merge'], rebin=4, xtitle='Multiplicity', logy=False), + "Fake_vs_En" : dict(ytitle=titles['fake'], rebin=4, xtitle='Energy [GeV]', logy=False), + "Fake_vs_Pt" : dict(ytitle=titles['fake'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), + "Fake_vs_Eta" : dict(ytitle=titles['fake'], rebin=None, xtitle=r'$\eta$', logy=False), + "Fake_vs_Phi" : dict(ytitle=titles['fake'], rebin=None, xtitle=r'$\phi$', logy=False), + "Fake_vs_Mult" : dict(ytitle=titles['fake'], rebin=4, xtitle='Multiplicity', logy=False), + "Dup_vs_En" : dict(ytitle=titles['dup'], rebin=4, xtitle='Energy [GeV]', logy=False), + "Dup_vs_Pt" : dict(ytitle=titles['dup'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), + "Dup_vs_Eta" : dict(ytitle=titles['dup'], rebin=None, xtitle=r'$\eta$', logy=False), + "Dup_vs_Phi" : dict(ytitle=titles['dup'], rebin=None, xtitle=r'$\phi$', logy=False), + "Dup_vs_Mult" : dict(ytitle=titles['dup'], rebin=4, xtitle='Multiplicity', logy=False), } for name, props in varsOverlay.items(): plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) varsResponse = { - ("ResponseEn_Sigma", "ResponseEn_Mean") : dict(name='ResolutionEn', ytitle=titles['resolution'], xtitle=r'$E [GeV]$', rebin=4, logy=False), - ("ResponseEnHits_Sigma", "ResponseEnHits_Mean") : dict(name='ResolutionEnHits', ytitle=titles['resolution'], xtitle=r'$E_{hits} [GeV]$', rebin=4, logy=False), - ("ResponseEnFrac_Sigma", "ResponseEnFrac_Mean") : dict(name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle=r'Energy Fraction', rebin=4, logy=False), - ("ResponsePt_Sigma", "ResponsePt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', rebin=4, logy=False), - ("ResponseEta_Sigma", "ResponseEta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', rebin=4, logy=False), - ("ResponsePhi_Sigma", "ResponsePhi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', rebin=None, logy=False), - ("ResponseMult_Sigma", "ResponseMult_Mean") : dict(name='ResolutionMult',ytitle=titles['resolution'], xtitle='Multiplicity', rebin=None, logy=False), + ("ResponseE_En_Sigma", "ResponseE_En_Mean") : dict(name='ResolutionEn', ytitle=titles['resolution'], xtitle=r'$E [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), + ("ResponseE_EnHits_Sigma", "ResponseE_EnHits_Mean") : dict(name='ResolutionEnHits', ytitle=titles['resolution'], xtitle=r'$E_{hits} [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), + ("ResponseE_EnFrac_Sigma", "ResponseE_EnFrac_Mean") : dict(name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle=r'Energy Fraction', rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), logy=False), + ("ResponseE_Pt_Sigma", "ResponseE_Pt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), + ("ResponseE_Eta_Sigma", "ResponseE_Eta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', rebin=None, logy=False), + ("ResponseE_Phi_Sigma", "ResponseE_Phi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', rebin=None, logy=False), + ("ResponseE_Mult_Sigma", "ResponseE_Mult_Mean") : dict(name='ResolutionMult',ytitle=titles['resolution'], xtitle='Multiplicity', rebin=4, logy=False), } for (num, den), props in varsResponse.items(): plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir=args.odir) - vars2D = { - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E_{hist}', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'Energy Fraction', unit='', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)) for subdir in subdirs}, - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -2., -1., 0., 1., 2., 3.15)) for subdir in subdirs}, - **{f'{subdir}/ResponseEn': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 45., 100., 200.)) for subdir in subdirs}, + vars2DProjection = { + **{f'{subdir}/ResponseE_En': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_EnHits': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$E_{hits}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_EnFrac': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'Energy Fraction', unit='', logy=False, rebin=(0., 0.1, 0.5, 0.9, 1.)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Pt': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Eta': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Phi': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -1.5, 0., 1.5, 3.15)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Mult': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 50., 100., 200.)) for subdir in subdirs}, 'SimClustersReconstructableEnFrac_Mult': dict(xtitle='Multiplicity', ytitle='# Clusters', var='Energy Fraction', unit='', logy=True, rebin=(0., 0.005, 0.01, 0.02, 0.03, 1.)), } - for name, props in vars2D.items(): + for name, props in vars2DProjection.items(): plotter = Plotter(args.sample_label, fontsize=15) root_hist = cached_histos[f"{name}"] plotProject(root_hist, props, rebin_edges=props['rebin'], outname=os.path.join(args.odir, name + '_Projected')) - xtitle, ytitle, var = props['xtitle'], props['ytitle'], props['var'] - props['xtitle'] = var - props['ytitle'] = xtitle - # props['var'] = ytitle + vars2D = { + 'SimClustersReconstructableEnFrac_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='Energy Fraction'), + 'simToRecoScore_En': dict(ytitle='Energy from SimTrack [GeV]', var='# Clusters', xtitle='SimToReco Score'), + 'simToRecoScore_EnHits': dict(ytitle='Energy from hits [GeV]', var='# Clusters', xtitle='SimToReco Score'), + 'simToRecoScore_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Score'), + 'simToRecoScore_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='SimToReco Score'), + 'simToRecoShEnF_En': dict(ytitle='Energy from SimTrack [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_EnHits': dict(ytitle='Energy from hits [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + } + + for name, props in vars2D.items(): + root_hist = cached_histos[f"{name}"] plot2D(root_hist, props, outname=os.path.join(args.odir, name)) + + vars1D = { + 'simToRecoShEnF': dict(xtitle='SimToReco Shared Energy Fraction', ytitle='# SimClusters', var='SimClusters', logy=False), + 'simToRecoScore': dict(xtitle='SimToReco Score', ytitle='# SimClusters', var='SimClusters', logy=False), + 'recoToSimScore': dict(xtitle='RecoToSim Score', ytitle='# SimClusters', var='SimClusters', logy=False), + } + + for name, props in vars1D.items(): + root_hist = cached_histos[f"{name}"] + plot1D(root_hist, props, outname=os.path.join(args.odir, name)) + + ################################################################################## + # Temporary hack to access CaloParticle histrograms + ################################################################################## + + dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/CaloParticles_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" + afile = ROOT.TFile.Open(args.file) + checkRootDir(afile, dqm_dir) + + debug('Start caching PFCluster histograms...') + subdirs = [] + cached_histos = {} + directory = afile.GetDirectory(dqm_dir) + for key in directory.GetListOfKeys(): + obj = key.ReadObj() + if isinstance(obj, ROOT.TDirectory): + subdirs.append(obj.GetName()) + subdir = afile.GetDirectory(f"{dqm_dir}/{obj.GetName()}") + for subkey in subdir.GetListOfKeys(): + name = subkey.GetName() + cached_histos[f"{obj.GetName()}/{name}"] = subkey.ReadObj() + else: + name = key.GetName() + cached_histos[name] = key.ReadObj() + debug(' ...done.') + + vars1D = { + 'CP_simToRecoShEnF': dict(xtitle='SimToReco Shared Energy Fraction', ytitle='# CaloParticles', var='CaloParticles', logy=False), + 'CP_simToRecoScore': dict(xtitle='SimToReco Score', ytitle='# CaloParticles', var='CaloParticles', logy=False), + 'CP_recoToSimScore': dict(xtitle='RecoToSim Score', ytitle='# CaloParticles', var='CaloParticles', logy=False), + } + + for name, props in vars1D.items(): + root_hist = cached_histos[f"{name}"] + plot1D(root_hist, props, outname=os.path.join(args.odir, name)) + + vars2D = { + 'CP_simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# CaloParticles', xtitle='SimToReco Shared Energy Fraction'), + } + + for name, props in vars2D.items(): + root_hist = cached_histos[f"{name}"] + plot2D(root_hist, props, outname=os.path.join(args.odir, name)) \ No newline at end of file From db08d42ee2a24d53b13174eb5d6a3de89a4eb8c2 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Mon, 10 Nov 2025 09:42:09 +0100 Subject: [PATCH 09/64] Redefine merge and split rates --- .../RecoParticleFlow/plugins/PFTester.cc | 423 +++++++----------- .../python/hltPFPostProcessor_cfi.py | 28 +- .../scripts/makeHLTPFValidationPlots.py | 68 +-- 3 files changed, 221 insertions(+), 298 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 308304e411853..051d6a910bcca 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -133,6 +133,8 @@ class PFTester : public DQMEDAnalyzer { UMap h_recoClustersReconstructable_; VUMap h_recoClustersMatchedSimClusters_; VUMap h_recoClustersMultiMatchedSimClusters_; + std::vector h_nSimMatchedToOneReco_; + std::vector h_nRecoMatchedToOneSim_; std::unordered_map> histo2dVarsReco = { {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, @@ -202,6 +204,8 @@ PFTester::PFTester(const edm::ParameterSet& iConfig) h2d_recoClustersMatchedSimClusters_.resize(nAssocScoreThresholds_); h2d_responsePt_.resize(nAssocScoreThresholds_); h2d_responseE_.resize(nAssocScoreThresholds_); + h_nSimMatchedToOneReco_.resize(nAssocScoreThresholds_); + h_nRecoMatchedToOneSim_.resize(nAssocScoreThresholds_); } void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { @@ -232,6 +236,15 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", 51, 0, 1.02, 220, 0., 1.1); h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 51, 0, 1.02, 200, 0., 200.); h_SimTrackToSimHitsEnergyFraction_ = ibook.book1D("SimTrackToSimHitsEnergyFraction", "SimTrackToSimHitsEnergyFraction;SimTrack to SimHits energy fraction", 110, 0, 1.1); + + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + h_nSimMatchedToOneReco_[ithr] = + ibook.book1D("nSimMatchedToOneReco", "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", 10, 0, 10); + h_nRecoMatchedToOneSim_[ithr] = + ibook.book1D("nRecoMatchedToOneSim", "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", 10, 0, 10); + } for (auto& hVar : histoVarsSim) { auto [nBins, hMin, hMax] = hVar.second; @@ -562,12 +575,11 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } // -------------------------------------------------------------------- - // ----- Efficiency and merge computation at cluster level ------------ + // ----- Efficiency and split computation at cluster level ------------ // -------------------------------------------------------------------- uint nSimClusters = 0; uint nSimClustersPrimary = 0; - std::vector recoIdsMerged; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { double energySumSimHits = 0; @@ -608,6 +620,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) if (simClusters[simId].g4Tracks()[0].isPrimary()) ++nSimClustersPrimary; + // efficiency and split denominator h_simClusters_["En"]->Fill(simClusters[simId].energy()); h_simClusters_["EnHits"]->Fill(energySumSimHits); h_simClusters_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); @@ -664,41 +677,40 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h2d_simClustersReconstructable_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); h2d_simClustersReconstructable_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); - std::vector wasNotFilled(nAssocScoreThresholds_, true); - for (const auto& recoPair : simToRecoMatched) { - const auto recoPairIdx = recoPair.first.index(); - - #ifdef debug - const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); - - auto ev = simClusters[simId].g4Tracks()[0].eventId().event(); - auto bx = simClusters[simId].g4Tracks()[0].eventId().bunchCrossing(); - edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx << ", en=" << energySumSimHits << ", hits="; - const auto& hits_fractions = simClusters[simId].hits_and_fractions(); - const auto& hits_energies = simClusters[simId].hits_and_energies(); - - auto itF = hits_fractions.begin(); - auto itE = hits_energies.begin(); - for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { - DetId id(itF->first); - const GlobalPoint pos = caloGeom.getPosition(id); - edm::LogPrint("PFTester") << " DetId=" << itF->first << ", eta=" << pos.eta() << ", phi=" << pos.phi() - << ", en=" << itE->second << ", fr=" << itF->second; - } - edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" - << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits - << ", score: " << recoPair.second.second << ", hits="; - for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { - DetId id(hit_energy.recHitRef()->detId()); - const GlobalPoint pos = caloGeom.getPosition(id); - edm:: LogPrint("PFTester") << " DetId=" << hit_energy.recHitRef()->detId() << ", eta=" << pos.eta() << ", phi=" << pos.phi() - << ", en=" << hit_energy.recHitRef()->energy() << ", fr=" << hit_energy.fraction(); - } - #endif + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + const double& thresh = assocScoreThresholds_[ithr]; - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - const double& thresh = assocScoreThresholds_[ithr]; + unsigned nRecoMatchedToOneSim = 0; + for (const auto& recoPair : simToRecoMatched) { + + #ifdef debug + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + + auto ev = simClusters[simId].g4Tracks()[0].eventId().event(); + auto bx = simClusters[simId].g4Tracks()[0].eventId().bunchCrossing(); + edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx << ", en=" << energySumSimHits << ", hits="; + const auto& hits_fractions = simClusters[simId].hits_and_fractions(); + const auto& hits_energies = simClusters[simId].hits_and_energies(); + + auto itF = hits_fractions.begin(); + auto itE = hits_energies.begin(); + for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { + DetId id(itF->first); + const GlobalPoint pos = caloGeom.getPosition(id); + edm::LogPrint("PFTester") << " DetId=" << itF->first << ", eta=" << pos.eta() << ", phi=" << pos.phi() + << ", en=" << itE->second << ", fr=" << itF->second; + } + edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" + << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first + << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", score: " << recoPair.second.second << ", hits="; + for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { + DetId id(hit_energy.recHitRef()->detId()); + const GlobalPoint pos = caloGeom.getPosition(id); + edm:: LogPrint("PFTester") << " DetId=" << hit_energy.recHitRef()->detId() << ", eta=" << pos.eta() << ", phi=" << pos.phi() + << ", en=" << hit_energy.recHitRef()->energy() << ", fr=" << hit_energy.fraction(); + } + #endif auto score = recoPair.second.second; auto shared_energy = recoPair.second.first; @@ -716,105 +728,72 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h_simToRecoScore_EnFrac_->Fill(score, SimClusterToCPEnergyFraction); h_simToRecoScore_Mult_->Fill(score, simClusters[simId].numberOfRecHits()); - if (score > thresh) - continue; // cut on shared energy fraction - // if (shared_energy_frac < thresh) - // continue; - - // numerator histograms must be filled only once per sim cluster - // they are filled inside the recoPair loop to enable a different denominator per threshold - if (wasNotFilled[ithr]) { - wasNotFilled[ithr] = false; - h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); - h_simClustersMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); - h_simClustersMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); - h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); - h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); - h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); - h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); - - h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(simClusters[simId].energy(), - simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), - simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Eta"]->Fill(energySumSimHits, - simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Phi"]->Fill(energySumSimHits, - simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Mult"]->Fill(energySumSimHits, - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, - simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, - simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), - simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), - simClusters[simId].phi()); - } + // if (shared_energy_frac > thresh) { + // nRecoMatchedToOneSim++; + // } - // discard reco clusters from merge counting if already considered for a previous sim cluster - const auto& mergeIt = std::find(recoIdsMerged.begin(), recoIdsMerged.end(), recoPairIdx); - if (mergeIt != recoIdsMerged.end()) - continue; - recoIdsMerged.push_back(recoPairIdx); - - const edm::Ref recoClusterRef(PFCluster, recoPairIdx); - const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); - assert(recoToSimIt != recoToSimAssoc.end()); - const auto& recoToSimMatched = recoToSimIt->val; - assert(!recoToSimMatched.empty()); - - // find how many sim clusters are associated to the matched reco cluster - unsigned nSimMerged = 0; - for (const auto& simPair : recoToSimMatched) { - if (simPair.second > thresh) - continue; - ++nSimMerged; + // cut on score + if (score < thresh) { + nRecoMatchedToOneSim++; } + } - if (nSimMerged > 1) { + // efficiency numerator + if (nRecoMatchedToOneSim > 0) { + h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); + h_simClustersMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); + h_simClustersMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); + h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); + h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); + h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + + h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); + + // split numerator + if (nRecoMatchedToOneSim > 1) { h_simClustersMultiMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); h_simClustersMultiMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); h_simClustersMultiMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClustersMultiMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); h_simClustersMultiMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); h_simClustersMultiMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); h_simClustersMultiMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); } - - #ifdef debug - for (const auto& recoPair : simToRecoMatched) { - edm::LogPrint("PFTester") << " simToRecoAssoc simCluster id " << simId << " : matched recoCluster id = " << recoPair.first.index() - << " shared energy = " << recoPair.second.first - << " score = " << recoPair.second.second << std::endl; - } - #endif } + + h_nRecoMatchedToOneSim_[ithr]->Fill(nRecoMatchedToOneSim); + } } h_nSimClusters_->Fill(nSimClusters); h_nSimClustersPrimary_->Fill(nSimClustersPrimary); - std::vector simIdsDuplicates; - + // -------------------------------------------------------------------- - // ----- Fakes and duplicates computation at cluster level ------------ + // ----- Fakes and merge computation at cluster level ----------------- // -------------------------------------------------------------------- h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { + + // fake and merge denominator h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); @@ -839,99 +818,78 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) if (recoToSimMatched.empty()) continue; - std::vector wasNotFilled(nAssocScoreThresholds_, true); - for (const auto& simPair : recoToSimMatched) { - const auto simPairIdx = simPair.first.index(); + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + const double& thresh = assocScoreThresholds_[ithr]; - #ifdef debug - edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx - << " score = " << simPair.second << std::endl; - #endif + unsigned nSimMatchedToOneReco = 0; - double energySumSimHits = 0; - for (auto hit_energy : simClusters[simPairIdx].hits_and_energies()) { - energySumSimHits += hit_energy.second; - } + for (const auto& simPair : recoToSimMatched) { + const auto simPairIdx = simPair.first.index(); - // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) - double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simPairIdx]; - if (SimClusterToCPEnergyFraction < enFracCut_) - continue; - // apply cut on pt of the sim track - if (simClusters[simPairIdx].pt() < ptCut_) - continue; + #ifdef debug + edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx + << " score = " << simPair.second << std::endl; + #endif - // filter all sim clusters produced by a sim track which crossed the - // tracker/calorimeter boundary outside the barrel - auto const scTrack = simClusters[simPairIdx].g4Tracks()[0]; - const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); - if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel - continue; + double energySumSimHits = 0; + for (auto hit_energy : simClusters[simPairIdx].hits_and_energies()) { + energySumSimHits += hit_energy.second; + } - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - const double& thresh = assocScoreThresholds_[ithr]; + // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) + double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simPairIdx]; + if (SimClusterToCPEnergyFraction < enFracCut_) + continue; + // apply cut on pt of the sim track + if (simClusters[simPairIdx].pt() < ptCut_) + continue; - h_recoToSimScore_->Fill(simPair.second); - if (simPair.second > thresh) + // filter all sim clusters produced by a sim track which crossed the + // tracker/calorimeter boundary outside the barrel + auto const scTrack = simClusters[simPairIdx].g4Tracks()[0]; + const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel continue; - // numerator histograms must be filled only once per reco cluster - if (wasNotFilled[ithr]) { - wasNotFilled[ithr] = false; - h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); - - h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].size()); - h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Eta"]->Fill(recoClusters[recoId].pt(), - recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Phi"]->Fill(recoClusters[recoId].pt(), - recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Mult"]->Fill(recoClusters[recoId].pt(), - recoClusters[recoId].size()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), - recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), - recoClusters[recoId].phi()); - } + h_recoToSimScore_->Fill(simPair.second); - // discard sim clusters from duplicate counting if already considered for a previous reco cluster - const auto& dupIt = std::find(simIdsDuplicates.begin(), simIdsDuplicates.end(), simPairIdx); - if (dupIt != simIdsDuplicates.end()) - continue; - simIdsDuplicates.push_back(simPairIdx); - - const edm::Ref simClusterRef(SimCluster, simPairIdx); - const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); - assert(simToRecoIt != simToRecoAssoc.end()); - const auto& simToRecoMatched = simToRecoIt->val; - assert(!simToRecoMatched.empty()); - - // find how many reco clusters are associated to the matched sim cluster - unsigned nRecoDuplicates = 0; - for (const auto& recoPair : simToRecoMatched) { - if (recoPair.second.second > thresh) - continue; - ++nRecoDuplicates; + // cut on score + if (simPair.second < thresh) { + nSimMatchedToOneReco++; } + } - if (nRecoDuplicates > 1) { + // fake numerator + if (nSimMatchedToOneReco > 0) { + h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); + h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); + h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); + h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); + + h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); + + // merge numerator + if (nSimMatchedToOneReco > 1) { h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); h_recoClustersMultiMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); } } + + h_nSimMatchedToOneReco_[ithr]->Fill(nSimMatchedToOneReco); + } } @@ -969,76 +927,37 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) if (simToRecoMatched.empty()) continue; + // they should already be sorted by score + std::vector simToRecoMatchedSorted(simToRecoMatched.begin(), simToRecoMatched.end()); + std::sort(simToRecoMatchedSorted.begin(), simToRecoMatchedSorted.end(), + [](const auto& a, const auto& b) { return a.second.second < b.second.second; }); + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; - bool fill = true; - - // check for duplicate reco clusters - unsigned recoId = 0; - unsigned nRecoDuplicates = 0; - for (const auto& recoPair : simToRecoMatched) { - if (recoPair.second.second > thresh) - continue; - ++nRecoDuplicates; - recoId = recoPair.first.index(); // used for the response computation - } - // only one reco cluster should be associated for the response - if (nRecoDuplicates != 1) { - fill = false; - } else { // check for merged sim cluster - - const edm::Ref recoClusterRef(PFCluster, recoId); - const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); - assert(recoToSimIt != recoToSimAssoc.end()); - const auto& recoToSimMatched = recoToSimIt->val; - assert(!recoToSimMatched.empty()); - - // find how many sim clusters are associated to the matched reco cluster - unsigned nSimMerged = 0; - for (const auto& simPair : recoToSimMatched) { - if (simPair.second > thresh) - continue; - ++nSimMerged; - } - - // only one sim cluster should be associated for the response - if (nSimMerged != 1) { - fill = false; + // fill only the best matched (lowest score) reco cluster, regardless split or merge + for (const auto& recoPair : simToRecoMatchedSorted) { + auto recoId = recoPair.first.index(); + + if (recoPair.second.second < thresh) { + h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_responsePt_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].pt() / simClusters[simId].pt()); + h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].pt() / simClusters[simId].pt()); + + h2d_responseE_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].energy() / energySumSimHits); + break; } } - - if (fill) { - h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), - recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, - recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, - recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), - recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, - recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Phi"]->Fill(simClusters[simId].phi(), - recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), - recoClusters[recoId].pt() / simClusters[simId].pt()); - - h2d_responseE_[ithr]["En"]->Fill(simClusters[simId].energy(), - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["EnHits"]->Fill(energySumSimHits, - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), - recoClusters[recoId].energy() / energySumSimHits); - } } } diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 69261dbc51dd8..ad5c0dd42b78d 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -55,14 +55,14 @@ f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", f"'Score{thr}/Eff_vs_Mult{name}' 'Efficiency vs Multiplicity {suf}' Score{thr}/SimClustersMatchedRecoClustersMult SimClusters{suf}Mult", - # Merge rate - f"'Score{thr}/Merge_vs_En{name}' 'Merge Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", - f"'Score{thr}/Merge_vs_EnHits{name}' 'Merge Rate vs Hits Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnHits SimClusters{suf}EnHits", - f"'Score{thr}/Merge_vs_EnFrac{name}' 'Merge Rate vs Energy fraction {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", - f"'Score{thr}/Merge_vs_Pt{name}' 'Merge Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", - f"'Score{thr}/Merge_vs_Eta{name}' 'Merge Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", - f"'Score{thr}/Merge_vs_Phi{name}' 'Merge Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", - f"'Score{thr}/Merge_vs_Mult{name}' 'Merge Rate vs Multiplicity {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClusters{suf}Mult", + # Split rate + f"'Score{thr}/Split_vs_En{name}' 'Split Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", + f"'Score{thr}/Split_vs_EnHits{name}' 'Split Rate vs Hits Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnHits SimClusters{suf}EnHits", + f"'Score{thr}/Split_vs_EnFrac{name}' 'Split Rate vs Energy fraction {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", + f"'Score{thr}/Split_vs_Pt{name}' 'Split Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", + f"'Score{thr}/Split_vs_Eta{name}' 'Split Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", + f"'Score{thr}/Split_vs_Phi{name}' 'Split Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", + f"'Score{thr}/Split_vs_Mult{name}' 'Split Rate vs Multiplicity {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClusters{suf}Mult", ) ], *[ item @@ -74,12 +74,12 @@ f"'Score{thr}/Fake_vs_Eta' 'Fake Rate vs #eta' Score{thr}/RecoClustersMatchedSimClustersEta RecoClustersEta fake", f"'Score{thr}/Fake_vs_Phi' 'Fake Rate vs #phi' Score{thr}/RecoClustersMatchedSimClustersPhi RecoClustersPhi fake", f"'Score{thr}/Fake_vs_Mult' 'Fake Rate vs Multiplicity' Score{thr}/RecoClustersMatchedSimClustersMult RecoClustersMult fake", - # Duplicate rate - f"'Score{thr}/Dup_vs_En' 'Dup Rate vs Energy' Score{thr}/RecoClustersMultiMatchedSimClustersEn RecoClustersEn", - f"'Score{thr}/Dup_vs_Pt' 'Dup Rate vs p_{{T}}' Score{thr}/RecoClustersMultiMatchedSimClustersPt RecoClustersPt", - f"'Score{thr}/Dup_vs_Eta' 'Dup Rate vs #eta' Score{thr}/RecoClustersMultiMatchedSimClustersEta RecoClustersEta", - f"'Score{thr}/Dup_vs_Phi' 'Dup Rate vs #phi' Score{thr}/RecoClustersMultiMatchedSimClustersPhi RecoClustersPhi", - f"'Score{thr}/Dup_vs_Mult' 'Dup Rate vs Mult' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClustersMult", + # Merge rate + f"'Score{thr}/Merge_vs_En' 'Merge Rate vs Energy' Score{thr}/RecoClustersMultiMatchedSimClustersEn RecoClustersEn", + f"'Score{thr}/Merge_vs_Pt' 'Merge Rate vs p_{{T}}' Score{thr}/RecoClustersMultiMatchedSimClustersPt RecoClustersPt", + f"'Score{thr}/Merge_vs_Eta' 'Merge Rate vs #eta' Score{thr}/RecoClustersMultiMatchedSimClustersEta RecoClustersEta", + f"'Score{thr}/Merge_vs_Phi' 'Merge Rate vs #phi' Score{thr}/RecoClustersMultiMatchedSimClustersPhi RecoClustersPhi", + f"'Score{thr}/Merge_vs_Mult' 'Merge Rate vs Mult' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClustersMult", ) ], ), diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 52bc5073dcb86..faaa446b95aba 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -233,13 +233,15 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): sublabel = re.sub(pattern, replacement, sub) if sublabel == 'score = 1.1': sublabel = 'score = 1.0' - if any(x in name for x in ('Eff', 'Fake', 'Dup', 'Merge')): + if any(x in name for x in ('Eff', 'Fake', 'Split', 'Merge')): eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) plotter.limits(y=(0,1.1), x=(bin_edges[0], bin_edges[-1])) else: + if "Response" in name: + plotter.limits(y=(0, 1.5)) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) @@ -298,8 +300,10 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) plotter.limits(x=(bin_edges[0], bin_edges[-1])) + if "Resolution" in props['name']: + plotter.limits(y=(0, 0.7)) plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') - plotter.ax.grid(color='grey', axis='x') + plotter.ax.grid(color='grey') plt.tight_layout() plotter.save( os.path.join(outdir, props['name']) ) @@ -351,13 +355,13 @@ def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, su if 'Fake' in name: values = np.array([1-i if i != 0 else np.nan for i in values]) - if any(x in name for x in ('Eff', 'Fake', 'Dup', 'Merge')): + if any(x in name for x in ('Eff', 'Fake', 'Split', 'Merge')): if 'Eff' in name: axis_name = 'Efficiency' elif 'Fake' in name: axis_name = 'Fake Rate' - elif 'Dup' in name: - axis_name = 'Duplicate Rate' + elif 'Split' in name: + axis_name = 'Split Rate' elif 'Merge' in name: axis_name = 'Merge Rate' ax2.set_ylabel(axis_name, color=eff_color) @@ -482,7 +486,7 @@ def __call__(self, parser, namespace, values, option_string=None): 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", 'eff': 'Efficiency', 'fake': 'Fake Rate', - 'dup': 'Duplicate Rate', + 'split': 'Split Rate', 'merge': 'Merge Rate'} dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" @@ -542,32 +546,32 @@ def __call__(self, parser, namespace, values, option_string=None): den=f'SimClusters{suf}Mult', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersMult', legnum='Matched SimClusters', xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - # Cluster merge rate (WIP) - f'{subdir}/Merge_vs_En{name}': dict(ratio=f'{subdir}/Merge_vs_En{name}', + # Cluster split rate + f'{subdir}/Split_vs_En{name}': dict(ratio=f'{subdir}/Split_vs_En{name}', den=f'SimClusters{suf}En', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEn', legnum='Multi Matched SimClusters', xtitle='Energy from SimTrack [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Merge_vs_EnHits{name}': dict(ratio=f'{subdir}/Merge_vs_EnHits{name}', + f'{subdir}/Split_vs_EnHits{name}': dict(ratio=f'{subdir}/Split_vs_EnHits{name}', den=f'SimClusters{suf}EnHits', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnHits', legnum='Multi Matched SimClusters', xtitle='Energy from hits [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Merge_vs_EnFrac{name}': dict(ratio=f'{subdir}/Merge_vs_EnFrac{name}', + f'{subdir}/Split_vs_EnFrac{name}': dict(ratio=f'{subdir}/Split_vs_EnFrac{name}', den=f'SimClusters{suf}EnFrac', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnFrac', legnum='Multi Matched SimClusters', xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Merge_vs_Pt{name}': dict(ratio=f'{subdir}/Merge_vs_Pt{name}', + f'{subdir}/Split_vs_Pt{name}': dict(ratio=f'{subdir}/Split_vs_Pt{name}', den=f'SimClusters{suf}Pt', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersPt', legnum='Multi Matched SimClusters', xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Merge_vs_Eta{name}': dict(ratio=f'{subdir}/Merge_vs_Eta{name}', + f'{subdir}/Split_vs_Eta{name}': dict(ratio=f'{subdir}/Split_vs_Eta{name}', den=f'SimClusters{suf}Eta', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEta', legnum='Multi Matched SimClusters', xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Merge_vs_Phi{name}': dict(ratio=f'{subdir}/Merge_vs_Phi{name}', + f'{subdir}/Split_vs_Phi{name}': dict(ratio=f'{subdir}/Split_vs_Phi{name}', den=f'SimClusters{suf}Phi', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersPhi', legnum='Multi Matched SimClusters', xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Merge_vs_Mult{name}': dict(ratio=f'{subdir}/Merge_vs_Mult{name}', + f'{subdir}/Split_vs_Mult{name}': dict(ratio=f'{subdir}/Split_vs_Mult{name}', den=f'SimClusters{suf}Mult', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersMult', legnum='Multi Matched SimClusters', xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), @@ -599,24 +603,24 @@ def __call__(self, parser, namespace, values, option_string=None): den=f'RecoClustersMult', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - # Cluster duplicate rate (WIP) - f'{subdir}/Dup_vs_En': dict(ratio=f'{subdir}/Dup_vs_En', + # Cluster merge rate (WIP) + f'{subdir}/Merge_vs_En': dict(ratio=f'{subdir}/Merge_vs_En', den=f'RecoClustersEn', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersEn', legnum='Multi Matched RecoClusters', xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Dup_vs_Pt': dict(ratio=f'{subdir}/Dup_vs_Pt', + f'{subdir}/Merge_vs_Pt': dict(ratio=f'{subdir}/Merge_vs_Pt', den=f'RecoClustersPt', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersPt', legnum='Multi Matched RecoClusters', xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Dup_vs_Eta': dict(ratio=f'{subdir}/Dup_vs_Eta', + f'{subdir}/Merge_vs_Eta': dict(ratio=f'{subdir}/Merge_vs_Eta', den=f'RecoClustersEta', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersEta', legnum='Multi Matched RecoClusters', xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Dup_vs_Phi': dict(ratio=f'{subdir}/Dup_vs_Phi', + f'{subdir}/Merge_vs_Phi': dict(ratio=f'{subdir}/Merge_vs_Phi', den=f'RecoClustersPhi', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersPhi', legnum='Multi Matched RecoClusters', xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Dup_vs_Mult': dict(ratio=f'{subdir}/Dup_vs_Mult', + f'{subdir}/Merge_vs_Mult': dict(ratio=f'{subdir}/Merge_vs_Mult', den=f'RecoClustersMult', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersMult', legnum='Multi Matched RecoClusters', xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), @@ -648,23 +652,23 @@ def __call__(self, parser, namespace, values, option_string=None): "Eff_vs_Phi_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), "Eff_vs_Mult" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), - "Merge_vs_En" : dict(ytitle=titles['merge'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), - "Merge_vs_EnHits" : dict(ytitle=titles['merge'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), - "Merge_vs_EnFrac" : dict(ytitle=titles['merge'], rebin=6, xtitle='Energy Fraction', logy=False), - "Merge_vs_Pt" : dict(ytitle=titles['merge'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Merge_vs_Eta" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\eta$', logy=False), - "Merge_vs_Phi" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\phi$', logy=False), - "Merge_vs_Mult" : dict(ytitle=titles['merge'], rebin=4, xtitle='Multiplicity', logy=False), + "Split_vs_En" : dict(ytitle=titles['split'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), + "Split_vs_EnHits" : dict(ytitle=titles['split'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), + "Split_vs_EnFrac" : dict(ytitle=titles['split'], rebin=6, xtitle='Energy Fraction', logy=False), + "Split_vs_Pt" : dict(ytitle=titles['split'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), + "Split_vs_Eta" : dict(ytitle=titles['split'], rebin=None, xtitle=r'$\eta$', logy=False), + "Split_vs_Phi" : dict(ytitle=titles['split'], rebin=None, xtitle=r'$\phi$', logy=False), + "Split_vs_Mult" : dict(ytitle=titles['split'], rebin=4, xtitle='Multiplicity', logy=False), "Fake_vs_En" : dict(ytitle=titles['fake'], rebin=4, xtitle='Energy [GeV]', logy=False), "Fake_vs_Pt" : dict(ytitle=titles['fake'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), "Fake_vs_Eta" : dict(ytitle=titles['fake'], rebin=None, xtitle=r'$\eta$', logy=False), "Fake_vs_Phi" : dict(ytitle=titles['fake'], rebin=None, xtitle=r'$\phi$', logy=False), "Fake_vs_Mult" : dict(ytitle=titles['fake'], rebin=4, xtitle='Multiplicity', logy=False), - "Dup_vs_En" : dict(ytitle=titles['dup'], rebin=4, xtitle='Energy [GeV]', logy=False), - "Dup_vs_Pt" : dict(ytitle=titles['dup'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Dup_vs_Eta" : dict(ytitle=titles['dup'], rebin=None, xtitle=r'$\eta$', logy=False), - "Dup_vs_Phi" : dict(ytitle=titles['dup'], rebin=None, xtitle=r'$\phi$', logy=False), - "Dup_vs_Mult" : dict(ytitle=titles['dup'], rebin=4, xtitle='Multiplicity', logy=False), + "Merge_vs_En" : dict(ytitle=titles['merge'], rebin=4, xtitle='Energy [GeV]', logy=False), + "Merge_vs_Pt" : dict(ytitle=titles['merge'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), + "Merge_vs_Eta" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\eta$', logy=False), + "Merge_vs_Phi" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\phi$', logy=False), + "Merge_vs_Mult" : dict(ytitle=titles['merge'], rebin=4, xtitle='Multiplicity', logy=False), } for name, props in varsOverlay.items(): plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) From ca28cdbdc244895769a04ec940b9724e683d092f Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Mon, 10 Nov 2025 10:22:38 +0100 Subject: [PATCH 10/64] Add sequence for association by shared energy score --- .../RecoParticleFlow/plugins/PFTester.cc | 42 +++++++++----- .../python/hltPFValidation_cfi.py | 55 ++++++++++--------- .../scripts/makeHLTPFValidationPlots.py | 46 ++++++++++++---- 3 files changed, 92 insertions(+), 51 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 051d6a910bcca..d29e27819789b 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -1,6 +1,3 @@ -// author: Mike Schmitt, University of Florida -// first version 11/7/2007 - #include "FWCore/Framework/interface/MakerMacros.h" #include "DQMServices/Core/interface/DQMEDAnalyzer.h" #include "DQMServices/Core/interface/DQMStore.h" @@ -103,6 +100,7 @@ class PFTester : public DQMEDAnalyzer { uint nAssocScoreThresholds_; double enFracCut_; double ptCut_; + bool doMatchByScore_; const std::unordered_map> histoVarsReco = { {"En", std::make_tuple(100, 0., 100.)}, @@ -194,7 +192,8 @@ PFTester::PFTester(const edm::ParameterSet& iConfig) iConfig.getParameter("PFClusterCaloParticleAssociator"))), assocScoreThresholds_(iConfig.getParameter>("assocScoreThresholds")), enFracCut_(iConfig.getParameter("enFracCut")), - ptCut_(iConfig.getParameter("ptCut")) { + ptCut_(iConfig.getParameter("ptCut")), + doMatchByScore_(iConfig.getParameter("doMatchByScore")) { nAssocScoreThresholds_ = assocScoreThresholds_.size(); h_simClustersMatchedRecoClusters_.resize(nAssocScoreThresholds_); h_simClustersMultiMatchedRecoClusters_.resize(nAssocScoreThresholds_); @@ -218,7 +217,8 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_CP_simToRecoShEnF_ = ibook.book1D("CP_simToRecoShEnF", "simToRecoSharedEnergy;CaloParticle Sim #rightarrow Reco shared energy fraction", 51, 0, 1.02); h_CP_simToRecoShEnF_Score_ = ibook.book2D("CP_simToRecoShEnF_Score", "CaloParticle #rightarrow PFCluster simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 51, 0, 1.02, 51, 0, 1.02); - std::string pfValidFolder = "HLT/ParticleFlow/PFClusterValidation_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_); + std::string matching = doMatchByScore_ ? "_MatchByScore" : "_MatchByShEnF"; + std::string pfValidFolder = "HLT/ParticleFlow/PFClusterValidation"+matching+"_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_); ibook.setCurrentFolder(pfValidFolder); h_nSimClusters_ = ibook.book1D("nSimClusters", "Number of SimClusters;Number of SimClusters per event", 100, 0, 100); h_nSimClustersPrimary_ = ibook.book1D("nSimClustersPrimary", "Number of Primary SimClusters;Number of Primary SimClusters per event", 100, 0, 100); @@ -728,15 +728,18 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h_simToRecoScore_EnFrac_->Fill(score, SimClusterToCPEnergyFraction); h_simToRecoScore_Mult_->Fill(score, simClusters[simId].numberOfRecHits()); - // cut on shared energy fraction - // if (shared_energy_frac > thresh) { - // nRecoMatchedToOneSim++; - // } - - // cut on score - if (score < thresh) { - nRecoMatchedToOneSim++; + if (doMatchByScore_) { + // cut on score + if (score < thresh) { + nRecoMatchedToOneSim++; + } + } else { + // cut on shared energy fraction + if (shared_energy_frac > thresh) { + nRecoMatchedToOneSim++; + } } + } // efficiency numerator @@ -939,7 +942,18 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) for (const auto& recoPair : simToRecoMatchedSorted) { auto recoId = recoPair.first.index(); - if (recoPair.second.second < thresh) { + bool passMatch = false; + if (doMatchByScore_) { + // cut on score + passMatch = (recoPair.second.second < thresh); + } else { + // cut on shared energy fraction + double shared_energy = recoPair.second.first; + double shared_energy_frac = shared_energy / energySumSimHits; + passMatch = (shared_energy_frac > thresh); + } + + if (passMatch) { h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].pt() / simClusters[simId].pt()); h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].pt() / simClusters[simId].pt()); h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].pt() / simClusters[simId].pt()); diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index e156fa067a8a4..5af786a3a10e9 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -33,45 +33,42 @@ PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.) ) -hltPFTesterECALWithCut1 = cms.EDProducer("PFTester", - PFCand = cms.InputTag("hltParticleFlowTmp"), - PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), - PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - CaloParticle = cms.InputTag("mix","MergedCaloTruth"), - SimCluster = cms.InputTag("mix","MergedCaloTruth"), - PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), - PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), - assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), +hltPFTesterECALWithCut1 = hltPFTesterECAL.clone( + enFracCut = cms.double(0.01), + ptCut = cms.double(0.) +) + +hltPFTesterECALWithCut2 = hltPFTesterECAL.clone( + enFracCut = cms.double(0.), + ptCut = cms.double(0.1) +) + +hltPFTesterECALWithCut3 = hltPFTesterECAL.clone( enFracCut = cms.double(0.01), + ptCut = cms.double(0.1) +) + +# SimToReco match based on shared energy fraction +hltPFTesterECALShEnF = hltPFTesterECAL.clone( + doMatchByScore = cms.bool(False) +) + +hltPFTesterECALShEnFWithCut1 = hltPFTesterECALShEnF.clone( + enFracCut = cms.double(0.01), ptCut = cms.double(0.) ) -hltPFTesterECALWithCut2 = cms.EDProducer("PFTester", - PFCand = cms.InputTag("hltParticleFlowTmp"), - PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), - PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - CaloParticle = cms.InputTag("mix","MergedCaloTruth"), - SimCluster = cms.InputTag("mix","MergedCaloTruth"), - PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), - PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), - assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), +hltPFTesterECALShEnFWithCut2 = hltPFTesterECALShEnF.clone( enFracCut = cms.double(0.), ptCut = cms.double(0.1) ) -hltPFTesterECALWithCut3 = cms.EDProducer("PFTester", - PFCand = cms.InputTag("hltParticleFlowTmp"), - PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), - PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - CaloParticle = cms.InputTag("mix","MergedCaloTruth"), - SimCluster = cms.InputTag("mix","MergedCaloTruth"), - PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), - PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), - assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), +hltPFTesterECALShEnFWithCut3 = hltPFTesterECALShEnF.clone( enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) @@ -85,4 +82,8 @@ +hltPFTesterECALWithCut1 +hltPFTesterECALWithCut2 +hltPFTesterECALWithCut3 + +hltPFTesterECALShEnF + +hltPFTesterECALShEnFWithCut1 + +hltPFTesterECALShEnFWithCut2 + +hltPFTesterECALShEnFWithCut3 ) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index faaa446b95aba..fcbc600d59d74 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -7,6 +7,7 @@ import re import matplotlib.pyplot as plt from matplotlib.transforms import blended_transform_factory +from matplotlib.colors import LogNorm plt.style.use('tableau-colorblind10') import array import ROOT @@ -205,7 +206,8 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly pattern = r"Score(\d+)p(\d+)" - replacement = lambda m: f"score = {m.group(1)}.{m.group(2)}" + matching = "score" if args.match_by_score else "shared energy fraction" + replacement = lambda m: f"{matching} = {m.group(1)}.{m.group(2)}" plotter = Plotter(args.sample_label, grid_color=None) for sub in subdirs: @@ -232,7 +234,7 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): baseline=None, color=next(colors_iter)) sublabel = re.sub(pattern, replacement, sub) - if sublabel == 'score = 1.1': sublabel = 'score = 1.0' + if sublabel == f'{matching} = 1.1': sublabel = f'{matching} = 1.0' if any(x in name for x in ('Eff', 'Fake', 'Split', 'Merge')): eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], @@ -259,7 +261,8 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly pattern = r"Score(\d+)p(\d+)" - replacement = lambda m: f"score = {m.group(1)}.{m.group(2)}" + matching = "score" if args.match_by_score else "shared energy fraction" + replacement = lambda m: f"{matching} = {m.group(1)}.{m.group(2)}" plotter = Plotter(args.sample_label, grid_color=None) for sub in subdirs: @@ -409,9 +412,22 @@ def plot2D(h, props, outname): nbins_x, nbins_y, x_edges, y_edges = define_bins_2D(h) values = histo_values_2D(h) - - pcm = plotter.ax.pcolormesh(x_edges, y_edges, np.where(values==0, np.nan, values), - cmap='viridis', shading='auto') + + values = np.where(values == 0., np.nan, values) + if 'logz' in props and props['logz']: + values_log = values[~np.isnan(values)] # avoid log(0) errors + pcm = plotter.ax.pcolormesh( + x_edges, y_edges, values, + cmap='viridis', + shading='auto', + norm=LogNorm(vmin=values_log.min(), vmax=values_log.max()) + ) + else: + pcm = plotter.ax.pcolormesh( + x_edges, y_edges, values, + cmap='viridis', + shading='auto' + ) plotter.labels(x=props['xtitle'], y=props['ytitle']) plotter.fig.colorbar(pcm, ax=plotter.ax, label=props['var']) @@ -461,6 +477,7 @@ def __call__(self, parser, namespace, values, option_string=None): parser.add_argument('-l', '--sample_label', default="", help='Sample label for plotting.') parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') parser.add_argument('--PtCut', default=0.01, help='Cut on the sim cluster energy fraction.') + parser.add_argument('--match_by_score', default=1, type=int, help='Use association based on score (if false, use shared energy fraction).') mutual_excl2 = parser.add_mutually_exclusive_group(required=True) mutual_excl2.add_argument('-f', '--file', help='Paths to the DQM ROOT file.') @@ -472,6 +489,10 @@ def __call__(self, parser, namespace, values, option_string=None): args = parser.parse_args() createDir(args.odir) + parentDir = os.path.dirname(args.odir) + if args.odir[-1] == '/': + parentDir = os.path.dirname(parentDir) + createIndexPHP(src=parentDir, dest=args.odir) fontsize = 16 colors = hep.style.CMS['axes.prop_cycle'].by_key()['color'] @@ -489,7 +510,12 @@ def __call__(self, parser, namespace, values, option_string=None): 'split': 'Split Rate', 'merge': 'Merge Rate'} - dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" + if args.match_by_score == 1: + matching = '_MatchByScore' + else: + matching = '_MatchByShEnF' + print("### INFO: Using association by shared energy fraction.") + dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation{matching}_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) @@ -711,7 +737,7 @@ def __call__(self, parser, namespace, values, option_string=None): 'simToRecoShEnF_EnHits': dict(ytitle='Energy from hits [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), 'simToRecoShEnF_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), 'simToRecoShEnF_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), - 'simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# Clusters', xtitle='SimToReco Shared Energy Fraction', logz=True), } for name, props in vars2D.items(): @@ -764,9 +790,9 @@ def __call__(self, parser, namespace, values, option_string=None): plot1D(root_hist, props, outname=os.path.join(args.odir, name)) vars2D = { - 'CP_simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# CaloParticles', xtitle='SimToReco Shared Energy Fraction'), + 'CP_simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# CaloParticles', xtitle='SimToReco Shared Energy Fraction', logz=True), } for name, props in vars2D.items(): root_hist = cached_histos[f"{name}"] - plot2D(root_hist, props, outname=os.path.join(args.odir, name)) \ No newline at end of file + plot2D(root_hist, props, outname=os.path.join(args.odir, name)) From 5c5600046a9291663324a751d99bf6057854fd3b Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 29 Oct 2025 11:20:33 +0100 Subject: [PATCH 11/64] Check HGCalGeometry to support RecHitTools in Run3 --- RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc b/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc index b98eb5c41f43e..43eb13cc412a4 100644 --- a/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc +++ b/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc @@ -113,7 +113,7 @@ void RecHitTools::setGeometry(const CaloGeometry& geom) { bhFirstLayer_ = bhOffset_ + (geomBH->topology().dddConstants()).firstLayer(); bhLastLayer_ = bhOffset_ + (geomBH->topology().dddConstants()).lastLayer(true); bhMaxIphi_ = geomBH->topology().dddConstants().maxCells(true); - } else { + } else if (static_cast(geom_->getSubdetectorGeometry(DetId::Forward, ForwardSubdetector::HGCEE))) { geometryType_ = 0; geomEE = static_cast(geom_->getSubdetectorGeometry(DetId::Forward, ForwardSubdetector::HGCEE)); From e2f10f8e105e220c0fe50b2637b9966ef92ca7bd Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Tue, 28 Oct 2025 15:07:05 +0100 Subject: [PATCH 12/64] Activate PF validation for Run3 --- .../EventContent/python/EventContent_cff.py | 8 ++ .../Common/python/HLTValidation_cff.py | 8 +- .../plugins/RecHitMapProducer.cc | 119 +++++++++--------- .../python/SimCalorimetry_EventContent_cff.py | 4 + .../python/caloTruthProducer_cfi.py | 5 +- .../MixingModule/python/digitizers_cfi.py | 3 + .../python/hltPFValidation_cfi.py | 6 +- .../scripts/makeHLTPFValidationPlots.py | 7 +- 8 files changed, 96 insertions(+), 64 deletions(-) diff --git a/Configuration/EventContent/python/EventContent_cff.py b/Configuration/EventContent/python/EventContent_cff.py index 3660d17f802d8..a5be0e573c1d4 100644 --- a/Configuration/EventContent/python/EventContent_cff.py +++ b/Configuration/EventContent/python/EventContent_cff.py @@ -695,6 +695,14 @@ def SwapKeepAndDrop(l): FEVTDEBUGHLTEventContent.outputCommands.append('keep *_*_MergedTrackTruth_*') FEVTDEBUGHLTEventContent.outputCommands.append('keep *_*_StripDigiSimLink_*') FEVTDEBUGHLTEventContent.outputCommands.append('keep *_*_PixelDigiSimLink_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowRecHitECALUnseeded_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowRecHitHBHE_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowClusterECALUnseeded_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowClusterHCAL_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowClusterHF_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlow_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowBlock_*_*') +FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltLightPFTracks_*_*') from Configuration.ProcessModifiers.hltClusterSplitting_cff import hltClusterSplitting hltClusterSplitting.toModify(FEVTDEBUGHLTEventContent, diff --git a/HLTriggerOffline/Common/python/HLTValidation_cff.py b/HLTriggerOffline/Common/python/HLTValidation_cff.py index e822a9d8659a5..04a54ce0f0cce 100644 --- a/HLTriggerOffline/Common/python/HLTValidation_cff.py +++ b/HLTriggerOffline/Common/python/HLTValidation_cff.py @@ -58,6 +58,7 @@ +ExoticaValidationProdSeq +hltMultiTrackValidationGsfTracks +hltJetPreValidSeq + +hltRecHitMapProducer ) from Configuration.Eras.Modifier_phase1Pixel_cff import phase1Pixel @@ -100,6 +101,7 @@ +SMPValidationSequence +hltbtagValidationSequence #too noisy for now +hltHCALdigisAnalyzer+hltHCALRecoAnalyzer+hltHCALNoiseRates # HCAL + +PFValSeq ) # Exclude everything except Muon and JetMET for now. Add HGCAL Hit Calibration @@ -107,7 +109,7 @@ HLTTauVal, egammaValidationSequence, heavyFlavorValidationSequence, - #HLTJetMETValSeq, + HLTJetMETValSeq, HLTSusyExoValSeq, HiggsValidationSequence, ExoticaValidationSequence, @@ -119,8 +121,8 @@ hltHCALNoiseRates]) _hltvalidationWithMC_Phase2.insert(-1, hgcalHitCalibrationHLT) _hltvalidationWithMC_Phase2.insert(-1, hltHgcalValidator) -_hltvalidationWithMC_Phase2.insert(-1, hltGENValidation) -_hltvalidationWithMC_Phase2.insert(-1, PFValSeq) +_hltvalidationWithMC_Phase2.insert(-1, hltGENValidation) # must go before HLTJetMETValSeq +_hltvalidationWithMC_Phase2.insert(-1, HLTJetMETValSeq) phase2_common.toReplaceWith(hltvalidationWithMC, _hltvalidationWithMC_Phase2) hltvalidationWithData = cms.Sequence( diff --git a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc index 006d649fbffb5..a77b7996b7d18 100644 --- a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc +++ b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc @@ -28,13 +28,14 @@ class RecHitMapProducer : public edm::global::EDProducer<> { std::vector> barrel_hits_token_; bool hgcalOnly_; + bool barrelOnly_; }; DEFINE_FWK_MODULE(RecHitMapProducer); using DetIdRecHitMap = std::unordered_map; -RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : hgcalOnly_(ps.getParameter("hgcalOnly")) { +RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : hgcalOnly_(ps.getParameter("hgcalOnly")), barrelOnly_(ps.getParameter("barrelOnly")) { std::vector tags = ps.getParameter>("hits"); for (auto& tag : tags) { if (tag.label().find("HGCalRecHit") != std::string::npos) { @@ -44,8 +45,10 @@ RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : hgcalOnly_(p } } - produces>("RefProdVectorHGCRecHitCollection"); - produces("hgcalRecHitMap"); + if (!barrelOnly_) { + produces>("RefProdVectorHGCRecHitCollection"); + produces("hgcalRecHitMap"); + } if (!hgcalOnly_) { produces>("RefProdVectorPFRecHitCollection"); @@ -60,68 +63,70 @@ void RecHitMapProducer::fillDescriptions(edm::ConfigurationDescriptions& descrip edm::InputTag("HGCalRecHit", "HGCHEFRecHits"), edm::InputTag("HGCalRecHit", "HGCHEBRecHits")}); desc.add("hgcalOnly", true); + desc.add("barrelOnly", false); descriptions.add("recHitMapProducer", desc); } void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::EventSetup& es) const { - auto hitMapHGCal = std::make_unique(); - - // Retrieve collections - assert(hgcal_hits_token_.size() == 3); - const auto& ee_hits = evt.getHandle(hgcal_hits_token_[0]); - const auto& fh_hits = evt.getHandle(hgcal_hits_token_[1]); - const auto& bh_hits = evt.getHandle(hgcal_hits_token_[2]); - - // Check validity of all handles - if (!ee_hits.isValid() || !fh_hits.isValid() || !bh_hits.isValid()) { - edm::LogWarning("HGCalRecHitMapProducer") << "One or more HGCal hit collections are unavailable. Returning an " - "empty map and an empty RefProdVectorHGCRecHitCollection"; - evt.put(std::make_unique>(), "RefProdVectorHGCRecHitCollection"); - evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); - } else { - // Fix order by storing a edm::RefProdVector - auto mcHGCRecHit = std::make_unique>(); - mcHGCRecHit->push_back(edm::RefProd(ee_hits)); - mcHGCRecHit->push_back(edm::RefProd(fh_hits)); - mcHGCRecHit->push_back(edm::RefProd(bh_hits)); - - edm::MultiSpan rechitSpan(*mcHGCRecHit); - for (unsigned int i = 0; i < rechitSpan.size(); ++i) { - const auto recHitDetId = rechitSpan[i].detid(); - hitMapHGCal->emplace(recHitDetId, i); + if (!barrelOnly_) { + auto hitMapHGCal = std::make_unique(); + + // Retrieve collections + assert(hgcal_hits_token_.size() == 3); + const auto& ee_hits = evt.getHandle(hgcal_hits_token_[0]); + const auto& fh_hits = evt.getHandle(hgcal_hits_token_[1]); + const auto& bh_hits = evt.getHandle(hgcal_hits_token_[2]); + + // Check validity of all handles + if (!ee_hits.isValid() || !fh_hits.isValid() || !bh_hits.isValid()) { + edm::LogWarning("HGCalRecHitMapProducer") << "One or more HGCal hit collections are unavailable. Returning an " + "empty map and an empty RefProdVectorHGCRecHitCollection"; + evt.put(std::make_unique>(), "RefProdVectorHGCRecHitCollection"); + evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); + } else { + // Fix order by storing a edm::RefProdVector + auto mcHGCRecHit = std::make_unique>(); + mcHGCRecHit->push_back(edm::RefProd(ee_hits)); + mcHGCRecHit->push_back(edm::RefProd(fh_hits)); + mcHGCRecHit->push_back(edm::RefProd(bh_hits)); + + edm::MultiSpan rechitSpan(*mcHGCRecHit); + for (unsigned int i = 0; i < rechitSpan.size(); ++i) { + const auto recHitDetId = rechitSpan[i].detid(); + hitMapHGCal->emplace(recHitDetId, i); + } + + evt.put(std::move(mcHGCRecHit), "RefProdVectorHGCRecHitCollection"); + evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); } - - evt.put(std::move(mcHGCRecHit), "RefProdVectorHGCRecHitCollection"); - evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); } - if (hgcalOnly_) { - return; - } + if (!hgcalOnly_) { + auto hitMapBarrel = std::make_unique(); - auto hitMapBarrel = std::make_unique(); - - assert(barrel_hits_token_.size() == 2); - const auto& ecal_hits = evt.getHandle(barrel_hits_token_[0]); - const auto& hbhe_hits = evt.getHandle(barrel_hits_token_[1]); - - if (!ecal_hits.isValid() || !hbhe_hits.isValid()) { - edm::LogWarning("HGCalRecHitMapProducer") << "One or more barrel hit collections are unavailable. Returning an " - "empty map and an empty RefProdVectorPFRecHitCollection"; - evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); - evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); - } else { - auto mcPFRecHit = std::make_unique>(); - mcPFRecHit->push_back(edm::RefProd(ecal_hits)); - mcPFRecHit->push_back(edm::RefProd(hbhe_hits)); - - edm::MultiSpan barrelRechitSpan(*mcPFRecHit); - for (unsigned int i = 0; i < barrelRechitSpan.size(); ++i) { - const auto recHitDetId = barrelRechitSpan[i].detId(); - hitMapBarrel->emplace(recHitDetId, i); - } + assert(barrel_hits_token_.size() == 2); + const auto& ecal_hits = evt.getHandle(barrel_hits_token_[0]); + const auto& hbhe_hits = evt.getHandle(barrel_hits_token_[1]); - evt.put(std::move(mcPFRecHit), "RefProdVectorPFRecHitCollection"); - evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); + if (!ecal_hits.isValid() || !hbhe_hits.isValid()) { + edm::LogWarning("HGCalRecHitMapProducer") + << "One or more barrel hit collections are unavailable. Returning an empty map."; + evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); + evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); + } else { + // Fix order by storing a edm::RefProdVector + auto mcPFRecHit = std::make_unique>(); + mcPFRecHit->push_back(edm::RefProd(ecal_hits)); + mcPFRecHit->push_back(edm::RefProd(hbhe_hits)); + + edm::MultiSpan barrelRechitSpan(*mcPFRecHit); + for (unsigned int i = 0; i < barrelRechitSpan.size(); ++i) { + const auto recHitDetId = barrelRechitSpan[i].detId(); + hitMapBarrel->emplace(recHitDetId, i); + } + + evt.put(std::move(mcPFRecHit), "RefProdVectorPFRecHitCollection"); + evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); + } } } diff --git a/SimCalorimetry/Configuration/python/SimCalorimetry_EventContent_cff.py b/SimCalorimetry/Configuration/python/SimCalorimetry_EventContent_cff.py index ae1ff68357822..54e55a46030b6 100644 --- a/SimCalorimetry/Configuration/python/SimCalorimetry_EventContent_cff.py +++ b/SimCalorimetry/Configuration/python/SimCalorimetry_EventContent_cff.py @@ -45,6 +45,10 @@ run2_common.toModify( SimCalorimetryFEVTDEBUG.outputCommands, func=lambda outputCommands: outputCommands.append('keep *_simHcalUnsuppressedDigis_*_*') ) run2_common.toModify( SimCalorimetryRAW.outputCommands, func=lambda outputCommands: outputCommands.append('keep *_simHcalUnsuppressedDigis_*_*') ) +from Configuration.Eras.Modifier_run3_common_cff import run3_common +run3_common.toModify( SimCalorimetryFEVTDEBUG.outputCommands, func=lambda outputCommands: outputCommands.append('keep *_mix_MergedCaloTruth_*') ) +run3_common.toModify( SimCalorimetryRAW.outputCommands, func=lambda outputCommands: outputCommands.append('keep *_mix_MergedCaloTruth_*') ) + from Configuration.Eras.Modifier_run3_ecal_devel_cff import run3_ecal_devel run3_ecal_devel.toModify(SimCalorimetryFEVTDEBUG.outputCommands, func=lambda outputCommands: outputCommands.append('keep *_mix_EBTimeDigi_*') ) run3_ecal_devel.toModify(SimCalorimetryRAW.outputCommands, func=lambda outputCommands: outputCommands.append('keep *_mix_EBTimeDigi_*') ) diff --git a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py index fad51271eee96..b110df69fce15 100644 --- a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py +++ b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py @@ -11,7 +11,7 @@ produceLegacySimCluster = cms.bool(True), produceBoundaryAndMergedSimCluster = cms.bool(True), premixStage1 = cms.bool(False), - doHGCAL = cms.bool(True), + doHGCAL = cms.bool(False), maximumPreviousBunchCrossing = cms.uint32(0), maximumSubsequentBunchCrossing = cms.uint32(0), simHitCollections = cms.PSet( @@ -31,6 +31,9 @@ ) ) +from Configuration.Eras.Modifier_phase2_common_cff import phase2_common +phase2_common.toModify(caloParticles, doHGCAL=True) + from Configuration.ProcessModifiers.premix_stage1_cff import premix_stage1 premix_stage1.toModify(caloParticles, premixStage1 = True) diff --git a/SimGeneral/MixingModule/python/digitizers_cfi.py b/SimGeneral/MixingModule/python/digitizers_cfi.py index 1eab8689f8c4c..565af87434d3c 100644 --- a/SimGeneral/MixingModule/python/digitizers_cfi.py +++ b/SimGeneral/MixingModule/python/digitizers_cfi.py @@ -34,6 +34,9 @@ ), mergedtruth = cms.PSet( trackingParticles + ), + calotruth = cms.PSet( + caloParticles ) ) diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 5af786a3a10e9..a86c245d65bf9 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -1,4 +1,5 @@ import FWCore.ParameterSet.Config as cms +from Validation.Configuration.hltHGCalSimValid_cff import hltRecHitMapProducer hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), @@ -25,7 +26,7 @@ ) hltPFTesterECAL = cms.EDProducer("PFTester", - PFCand = cms.InputTag("hltParticleFlowTmp"), + PFCand = cms.InputTag("hltParticleFlow"), PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), CaloParticle = cms.InputTag("mix","MergedCaloTruth"), @@ -73,6 +74,9 @@ ptCut = cms.double(0.1) ) +from Configuration.Eras.Modifier_phase2_common_cff import phase2_common +phase2_common.toModify(hltPFTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp")) + PFValSeq = cms.Sequence( hltPFScAssocByEnergyScoreProducer +hltPFClusterSimClusterAssociationProducerECAL diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index fcbc600d59d74..7642e0c8dec72 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -103,8 +103,10 @@ def __init__(self, label, fontsize=18, grid_color='grey'): self._fig, self._ax = plt.subplots(figsize=(10, 10)) self.fontsize = fontsize - hep.cms.text(' Phase-2 Simulation Preliminary', ax=self._ax, fontsize=fontsize) - hep.cms.lumitext(label + " | 14 TeV", ax=self._ax, fontsize=fontsize) + if args.era == 'Phase2': era='Phase-2'; en='14' + elif args.era == 'Run3': era='Run-3'; en='13.6' + hep.cms.text(f' {era} Simulation Preliminary', ax=self._ax, fontsize=fontsize) + hep.cms.lumitext(label + f" | {en} TeV", ax=self._ax, fontsize=fontsize) if grid_color: self._ax.grid(which='major', color=grid_color) @@ -475,6 +477,7 @@ def __call__(self, parser, namespace, values, option_string=None): parser = argparse.ArgumentParser(description='Make HLT PF validation plots. \nExample command:\n' + full_command) parser.add_argument('-o', '--odir', default="HLTPFValidationPlots", help='Path to the output directory.') parser.add_argument('-l', '--sample_label', default="", help='Sample label for plotting.') + parser.add_argument('-e', '--era', default="Phase2", help="Chose between ['Phase2', 'Run3'].") parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') parser.add_argument('--PtCut', default=0.01, help='Cut on the sim cluster energy fraction.') parser.add_argument('--match_by_score', default=1, type=int, help='Use association based on score (if false, use shared energy fraction).') From 832a3db3bcc465414e31e67571d711dcd58bf776 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Tue, 25 Nov 2025 09:53:41 +0100 Subject: [PATCH 13/64] Template PFTester to support ticl barrel collections --- .../Common/python/HLTValidation_cff.py | 2 +- .../python/hgcHitAssociation_cfi.py | 6 + .../python/hltHGCalSimValid_cff.py | 76 +++ .../python/HLTHGCalValidator_cff.py | 67 +++ .../RecoParticleFlow/plugins/PFTester.cc | 495 +++++++++++------- .../python/hltPFPostProcessor_cfi.py | 12 +- .../python/hltPFValidation_cfi.py | 50 +- .../scripts/makeHLTPFValidationPlots.py | 20 +- 8 files changed, 511 insertions(+), 217 deletions(-) diff --git a/HLTriggerOffline/Common/python/HLTValidation_cff.py b/HLTriggerOffline/Common/python/HLTValidation_cff.py index 04a54ce0f0cce..d94c36371baaa 100644 --- a/HLTriggerOffline/Common/python/HLTValidation_cff.py +++ b/HLTriggerOffline/Common/python/HLTValidation_cff.py @@ -120,7 +120,7 @@ hltHCALRecoAnalyzer, hltHCALNoiseRates]) _hltvalidationWithMC_Phase2.insert(-1, hgcalHitCalibrationHLT) -_hltvalidationWithMC_Phase2.insert(-1, hltHgcalValidator) +_hltvalidationWithMC_Phase2.insert(-1, hltHgcalValSeq) _hltvalidationWithMC_Phase2.insert(-1, hltGENValidation) # must go before HLTJetMETValSeq _hltvalidationWithMC_Phase2.insert(-1, HLTJetMETValSeq) phase2_common.toReplaceWith(hltvalidationWithMC, _hltvalidationWithMC_Phase2) diff --git a/SimCalorimetry/HGCalSimProducers/python/hgcHitAssociation_cfi.py b/SimCalorimetry/HGCalSimProducers/python/hgcHitAssociation_cfi.py index 21037728b61b4..1e00e39aa7d32 100644 --- a/SimCalorimetry/HGCalSimProducers/python/hgcHitAssociation_cfi.py +++ b/SimCalorimetry/HGCalSimProducers/python/hgcHitAssociation_cfi.py @@ -1,11 +1,17 @@ import FWCore.ParameterSet.Config as cms from SimCalorimetry.HGCalAssociatorProducers.hgCalLCToCPAssociatorByEnergyScoreProducer_cfi import hgCalLCToCPAssociatorByEnergyScoreProducer as _lcAssocByEnergyScoreProducer from SimCalorimetry.HGCalAssociatorProducers.hgCalLCToSCAssociatorByEnergyScoreProducer_cfi import hgCalLCToSCAssociatorByEnergyScoreProducer as _scAssocByEnergyScoreProducer +from SimCalorimetry.HGCalAssociatorProducers.barrelLCToCPAssociatorByEnergyScoreProducer_cfi import barrelLCToCPAssociatorByEnergyScoreProducer as _barrelLcAssocByEnergyScoreProducer +from SimCalorimetry.HGCalAssociatorProducers.barrelLCToSCAssociatorByEnergyScoreProducer_cfi import barrelLCToSCAssociatorByEnergyScoreProducer as _barrelScAssocByEnergyScoreProducer lcAssocByEnergyScoreProducer = _lcAssocByEnergyScoreProducer.clone(hardScatterOnly = cms.bool(True)) scAssocByEnergyScoreProducer = _scAssocByEnergyScoreProducer.clone(hardScatterOnly = cms.bool(True)) +barrelLcAssocByEnergyScoreProducer = _barrelLcAssocByEnergyScoreProducer.clone(hardScatterOnly = cms.bool(True)) +barrelScAssocByEnergyScoreProducer = _barrelScAssocByEnergyScoreProducer.clone(hardScatterOnly = cms.bool(True)) from Configuration.ProcessModifiers.enableCPfromPU_cff import enableCPfromPU enableCPfromPU.toModify(lcAssocByEnergyScoreProducer, hardScatterOnly = cms.bool(False)) enableCPfromPU.toModify(scAssocByEnergyScoreProducer, hardScatterOnly = cms.bool(False)) +enableCPfromPU.toModify(barrelLcAssocByEnergyScoreProducer, hardScatterOnly = cms.bool(False)) +enableCPfromPU.toModify(barrelScAssocByEnergyScoreProducer, hardScatterOnly = cms.bool(False)) diff --git a/Validation/Configuration/python/hltHGCalSimValid_cff.py b/Validation/Configuration/python/hltHGCalSimValid_cff.py index 634c929d1337e..d7822a28eda90 100644 --- a/Validation/Configuration/python/hltHGCalSimValid_cff.py +++ b/Validation/Configuration/python/hltHGCalSimValid_cff.py @@ -5,6 +5,11 @@ from SimCalorimetry.HGCalAssociatorProducers.LCToSCAssociation_cfi import layerClusterSimClusterAssociation as _layerClusterSimClusterAssociationProducer from SimCalorimetry.HGCalAssociatorProducers.LCToCPAssociation_cfi import layerClusterCaloParticleAssociation as _layerClusterCaloParticleAssociationProducer +from SimCalorimetry.HGCalSimProducers.hgcHitAssociation_cfi import barrelLcAssocByEnergyScoreProducer as _barrelLcAssocByEnergyScoreProducer +from SimCalorimetry.HGCalSimProducers.hgcHitAssociation_cfi import barrelScAssocByEnergyScoreProducer as _barrelScAssocByEnergyScoreProducer +from SimCalorimetry.HGCalAssociatorProducers.LCToSCAssociation_cfi import barrelLayerClusterSimClusterAssociation as _barrelLayerClusterSimClusterAssociation +from SimCalorimetry.HGCalAssociatorProducers.LCToCPAssociation_cfi import barrelLayerClusterCaloParticleAssociation as _barrelLayerClusterCaloParticleAssociation + from SimCalorimetry.HGCalAssociatorProducers.SimClusterToCaloParticleAssociation_cfi import SimClusterToCaloParticleAssociation from SimCalorimetry.HGCalAssociatorProducers.TSToSimTSAssociation_cfi import allTrackstersToSimTrackstersAssociationsByLCs as _allTrackstersToSimTrackstersAssociationsByLCs from SimCalorimetry.HGCalAssociatorProducers.hitToSimClusterCaloParticleAssociator_cfi import hitToSimClusterCaloParticleAssociator as _hitToSimClusterCaloParticleAssociator @@ -30,6 +35,76 @@ hits = [*hgcal_hits, *barrel_hits], ) +# LC to CP and LC to SC associators TICL-based for HGCal region + +hltLcAssocByEnergyScoreProducer = _lcAssocByEnergyScoreProducer.clone( + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorHGCRecHitCollection"), + hitMapTag = cms.InputTag("hltRecHitMapProducer","hgcalRecHitMap"), +) + +hltScAssocByEnergyScoreProducer = _scAssocByEnergyScoreProducer.clone( + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorHGCRecHitCollection"), + hitMapTag = cms.InputTag("hltRecHitMapProducer","hgcalRecHitMap"), +) + +hltLayerClusterCaloParticleAssociationProducer = _layerClusterCaloParticleAssociationProducer.clone( + associator = cms.InputTag("hltLcAssocByEnergyScoreProducer"), + label_lc = cms.InputTag("hltMergeLayerClusters") +) + +hltLayerClusterSimClusterAssociationProducer = _layerClusterSimClusterAssociationProducer.clone( + associator = cms.InputTag("hltScAssocByEnergyScoreProducer"), + label_lcl = cms.InputTag("hltMergeLayerClusters") +) + +hltHgcalLayerClustersAssociatorsTask = cms.Task( + hltLcAssocByEnergyScoreProducer, + hltScAssocByEnergyScoreProducer, + hltLayerClusterCaloParticleAssociationProducer, + hltLayerClusterSimClusterAssociationProducer, +) + +# LC to CP and LC to SC associators TICL-based for barrel region (ticl_barrel) + +hltBarrelLcAssocByEnergyScoreProducer = _barrelLcAssocByEnergyScoreProducer.clone( + hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), + hitMapTag = cms.InputTag("hltRecHitMapProducer","barrelRecHitMap"), +) + +hltBarrelScAssocByEnergyScoreProducer = _barrelScAssocByEnergyScoreProducer.clone( + hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), + hitMapTag = cms.InputTag("hltRecHitMapProducer","barrelRecHitMap"), +) + +hltBarrelLayerClusterCaloParticleAssociationProducer = _barrelLayerClusterCaloParticleAssociation.clone( + associator = cms.InputTag("hltBarrelLcAssocByEnergyScoreProducer"), + label_lc = cms.InputTag("hltBarrelLayerClustersEB") +) + +hltBarrelLayerClusterSimClusterAssociationProducer = _barrelLayerClusterSimClusterAssociation.clone( + associator = cms.InputTag("hltBarrelScAssocByEnergyScoreProducer"), + label_lcl = cms.InputTag("hltBarrelLayerClustersEB") +) + +hltHgcalAndBarrelLayerClustersAssociatorsTask = cms.Task( + hltLcAssocByEnergyScoreProducer, + hltScAssocByEnergyScoreProducer, + hltLayerClusterCaloParticleAssociationProducer, + hltLayerClusterSimClusterAssociationProducer, + hltBarrelLcAssocByEnergyScoreProducer, + hltBarrelScAssocByEnergyScoreProducer, + hltBarrelLayerClusterCaloParticleAssociationProducer, + hltBarrelLayerClusterSimClusterAssociationProducer, +) + +from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel +ticl_barrel.toReplaceWith( + hltHgcalLayerClustersAssociatorsTask, + hltHgcalAndBarrelLayerClustersAssociatorsTask +) + +# LC to Tracksters and Tracksters to SimTracksters associators TICL-based for HGCal region + from SimCalorimetry.HGCalAssociatorProducers.AllLayerClusterToTracksterAssociatorsProducer_cfi import AllLayerClusterToTracksterAssociatorsProducer as _AllLayerClusterToTracksterAssociatorsProducer hltAllLayerClusterToTracksterAssociations = _AllLayerClusterToTracksterAssociatorsProducer.clone( @@ -78,6 +153,7 @@ hltHGCalLCToCPAssociatorByEnergyScoreProducer, hltHGCalLCToSCAssociatorByEnergyScoreProducer, SimClusterToCaloParticleAssociation, + hltHgcalLayerClustersAssociatorsTask, hltHGCalLayerClusterCaloParticleAssociation, hltHGCalLayerClusterSimClusterAssociation, hltAllLayerClusterToTracksterAssociations, diff --git a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py index 438a217b6ff9a..f3497a3960036 100644 --- a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py +++ b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py @@ -47,3 +47,70 @@ mergeRecoToSimAssociator = cms.InputTag("hltAllTrackstersToSimTrackstersAssociationsByLCs:hltTiclCandidateTohltTiclSimTrackstersfromCPs"), ) +hltLayerClusterTesterECAL = cms.EDProducer("CaloClusterTester", + PFCand = cms.InputTag("hltParticleFlowTmp"), + Rechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), + RecoCluster = cms.InputTag("hltBarrelLayerClustersEB"), + SimCluster = cms.InputTag("mix","MergedCaloTruth"), + CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + ClusterSimClusterAssociator = cms.InputTag("hltBarrelLayerClusterSimClusterAssociationProducer"), + ClusterCaloParticleAssociator = cms.InputTag("hltBarrelLayerClusterCaloParticleAssociationProducer"), + outFolder = cms.string('HLT/TiclBarrel'), + assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + doMatchByScore = cms.bool(True), + enFracCut = cms.double(0.), + ptCut = cms.double(0.) +) + +hltLayerClusterTesterECALWithCut1 = hltLayerClusterTesterECAL.clone( + enFracCut = cms.double(0.01), + ptCut = cms.double(0.) +) + +hltLayerClusterTesterECALWithCut2 = hltLayerClusterTesterECAL.clone( + enFracCut = cms.double(0.), + ptCut = cms.double(0.1) +) + +hltLayerClusterTesterECALWithCut3 = hltLayerClusterTesterECAL.clone( + enFracCut = cms.double(0.01), + ptCut = cms.double(0.1) +) + +# SimToReco match based on shared energy fraction +hltLayerClusterTesterECALShEnF = hltLayerClusterTesterECAL.clone( + doMatchByScore = cms.bool(False) +) + +hltLayerClusterTesterECALShEnFWithCut1 = hltLayerClusterTesterECALShEnF.clone( + enFracCut = cms.double(0.01), + ptCut = cms.double(0.) +) + +hltLayerClusterTesterECALShEnFWithCut2 = hltLayerClusterTesterECALShEnF.clone( + enFracCut = cms.double(0.), + ptCut = cms.double(0.1) +) + +hltLayerClusterTesterECALShEnFWithCut3 = hltLayerClusterTesterECALShEnF.clone( + enFracCut = cms.double(0.01), + ptCut = cms.double(0.1) +) + +hltHgcalValSeq = cms.Sequence( + hltHgcalValidator) + +hltHgcalAndBarrelValSeq = cms.Sequence( + hltHgcalValidator + +hltLayerClusterTesterECAL + +hltLayerClusterTesterECALWithCut1 + +hltLayerClusterTesterECALWithCut2 + +hltLayerClusterTesterECALWithCut3 + +hltLayerClusterTesterECALShEnF + +hltLayerClusterTesterECALShEnFWithCut1 + +hltLayerClusterTesterECALShEnFWithCut2 + +hltLayerClusterTesterECALShEnFWithCut3 +) + +from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel +ticl_barrel.toReplaceWith(hltHgcalValSeq, hltHgcalAndBarrelValSeq) \ No newline at end of file diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index d29e27819789b..2feb00d592ec6 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -10,6 +10,7 @@ #include "DataFormats/ParticleFlowReco/interface/PFBlockElement.h" #include "DataFormats/ParticleFlowReco/interface/PFRecTrack.h" #include "DataFormats/ParticleFlowReco/interface/PFCluster.h" +#include "DataFormats/CaloRecHit/interface/CaloCluster.h" #include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" #include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" @@ -22,9 +23,10 @@ #include "Geometry/Records/interface/CaloGeometryRecord.h" #include "DataFormats/GeometryVector/interface/GlobalPoint.h" -class PFTester : public DQMEDAnalyzer { +template +class PFTesterT : public DQMEDAnalyzer { public: - explicit PFTester(const edm::ParameterSet&); + explicit PFTesterT(const edm::ParameterSet&); protected: void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; @@ -33,14 +35,14 @@ class PFTester : public DQMEDAnalyzer { edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; - edm::EDGetTokenT PFRechitToken_; - edm::EDGetTokenT PFClusterToken_; + edm::EDGetTokenT RechitToken_; + edm::EDGetTokenT RecoClusterToken_; edm::EDGetTokenT CaloParticleToken_; edm::EDGetTokenT SimClusterToken_; - edm::EDGetTokenT> RecoToSimAssociatorToken_; - edm::EDGetTokenT> SimToRecoAssociatorToken_; - edm::EDGetTokenT> RecoToCpAssociatorToken_; - edm::EDGetTokenT> CpToRecoAssociatorToken_; + edm::EDGetTokenT> RecoToSimAssociatorToken_; + edm::EDGetTokenT> SimToRecoAssociatorToken_; + edm::EDGetTokenT> RecoToCpAssociatorToken_; + edm::EDGetTokenT> CpToRecoAssociatorToken_; MonitorElement* h_PFCandEt_; MonitorElement* h_PFCandEta_; @@ -78,10 +80,10 @@ class PFTester : public DQMEDAnalyzer { MonitorElement* h_CP_simToRecoScore_; MonitorElement* h_CP_simToRecoShEnF_; MonitorElement* h_CP_simToRecoShEnF_Score_; - + MonitorElement* h_nPFClusters_; - MonitorElement* h_nSimClusters_; - MonitorElement* h_nSimClustersPrimary_; + MonitorElement* h_nSimClusters_; + MonitorElement* h_nSimClustersPrimary_; MonitorElement* h_recoToSimScore_; MonitorElement* h_simToRecoScore_; MonitorElement* h_simToRecoShEnF_; @@ -101,11 +103,12 @@ class PFTester : public DQMEDAnalyzer { double enFracCut_; double ptCut_; bool doMatchByScore_; + std::string outFolder_; const std::unordered_map> histoVarsReco = { {"En", std::make_tuple(100, 0., 100.)}, {"Pt", std::make_tuple(200, 0., 100.)}, - {"PtLow", std::make_tuple(100, 0., 10.)}, + {"PtLow", std::make_tuple(100, 0., 10.)}, {"Eta", std::make_tuple(50, -6.5, 6.5)}, {"Phi", std::make_tuple(50, -3.5, 3.5)}, {"Mult", std::make_tuple(200, 0., 200.)}, @@ -115,7 +118,7 @@ class PFTester : public DQMEDAnalyzer { {"EnHits", std::make_tuple(100, 0., 100.)}, {"EnFrac", std::make_tuple(220, 0., 1.1)}, {"Pt", std::make_tuple(200, 0., 100.)}, - {"PtLow", std::make_tuple(100, 0., 10.)}, + {"PtLow", std::make_tuple(100, 0., 10.)}, {"Eta", std::make_tuple(50, -6.5, 6.5)}, {"Phi", std::make_tuple(50, -3.5, 3.5)}, {"Mult", std::make_tuple(200, 0., 200.)}, @@ -175,25 +178,27 @@ class PFTester : public DQMEDAnalyzer { VU2Map h2d_responseE_; }; -PFTester::PFTester(const edm::ParameterSet& iConfig) +template +PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) : geometry_token_(esConsumes()), PFCandToken_(consumes(iConfig.getParameter("PFCand"))), - PFRechitToken_(consumes(iConfig.getParameter("PFRechit"))), - PFClusterToken_(consumes(iConfig.getParameter("PFCluster"))), + RechitToken_(consumes(iConfig.getParameter("Rechit"))), + RecoClusterToken_(consumes(iConfig.getParameter("RecoCluster"))), CaloParticleToken_(consumes(iConfig.getParameter("CaloParticle"))), SimClusterToken_(consumes(iConfig.getParameter("SimCluster"))), - RecoToSimAssociatorToken_(consumes>( - iConfig.getParameter("PFClusterSimClusterAssociator"))), - SimToRecoAssociatorToken_(consumes>( - iConfig.getParameter("PFClusterSimClusterAssociator"))), - RecoToCpAssociatorToken_(consumes>( - iConfig.getParameter("PFClusterCaloParticleAssociator"))), - CpToRecoAssociatorToken_(consumes>( - iConfig.getParameter("PFClusterCaloParticleAssociator"))), + RecoToSimAssociatorToken_(consumes>( + iConfig.getParameter("ClusterSimClusterAssociator"))), + SimToRecoAssociatorToken_(consumes>( + iConfig.getParameter("ClusterSimClusterAssociator"))), + RecoToCpAssociatorToken_(consumes>( + iConfig.getParameter("ClusterCaloParticleAssociator"))), + CpToRecoAssociatorToken_(consumes>( + iConfig.getParameter("ClusterCaloParticleAssociator"))), assocScoreThresholds_(iConfig.getParameter>("assocScoreThresholds")), enFracCut_(iConfig.getParameter("enFracCut")), ptCut_(iConfig.getParameter("ptCut")), - doMatchByScore_(iConfig.getParameter("doMatchByScore")) { + doMatchByScore_(iConfig.getParameter("doMatchByScore")), + outFolder_(iConfig.getParameter("outFolder")) { nAssocScoreThresholds_ = assocScoreThresholds_.size(); h_simClustersMatchedRecoClusters_.resize(nAssocScoreThresholds_); h_simClustersMultiMatchedRecoClusters_.resize(nAssocScoreThresholds_); @@ -207,45 +212,148 @@ PFTester::PFTester(const edm::ParameterSet& iConfig) h_nRecoMatchedToOneSim_.resize(nAssocScoreThresholds_); } -void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { +template +void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { + ibook.setCurrentFolder(outFolder_ + "/CaloParticles_EnFracCut" + doubleToString(enFracCut_) + "_PtCut" + + doubleToString(ptCut_)); + h_CaloParticleToSimClusterEnergyFraction_ = + ibook.book1D("CaloParticleToSimClusterEnergyFraction", + "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", + 100, + 0, + 2); + h_CaloParticleToSimHitsEnergyFraction_ = + ibook.book1D("CaloParticleToSimHitsEnergyFraction", + "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", + 100, + 0, + 2); + h_CP_recoToSimScore_ = + ibook.book1D("CP_recoToSimScore", "recoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); + h_CP_simToRecoScore_ = + ibook.book1D("CP_simToRecoScore", "simToRecoScore;CaloParticle Sim #rightarrow Reco score", 51, 0, 1.02); + h_CP_simToRecoShEnF_ = ibook.book1D("CP_simToRecoShEnF", + "simToRecoSharedEnergy;CaloParticle Sim #rightarrow Reco shared energy fraction", + 51, + 0, + 1.02); + h_CP_simToRecoShEnF_Score_ = ibook.book2D("CP_simToRecoShEnF_Score", + "CaloParticle #rightarrow RecoCluster simToRecoSharedEnergy_Score;Sim " + "#rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", + 51, + 0, + 1.02, + 51, + 0, + 1.02); - ibook.setCurrentFolder("HLT/ParticleFlow/CaloParticles_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_)); - h_CaloParticleToSimClusterEnergyFraction_ = ibook.book1D("CaloParticleToSimClusterEnergyFraction", "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", 100, 0, 2); - h_CaloParticleToSimHitsEnergyFraction_ = ibook.book1D("CaloParticleToSimHitsEnergyFraction", "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", 100, 0, 2); - h_CP_recoToSimScore_ = ibook.book1D("CP_recoToSimScore", "recoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); - h_CP_simToRecoScore_ = ibook.book1D("CP_simToRecoScore", "simToRecoScore;CaloParticle Sim #rightarrow Reco score", 51, 0, 1.02); - h_CP_simToRecoShEnF_ = ibook.book1D("CP_simToRecoShEnF", "simToRecoSharedEnergy;CaloParticle Sim #rightarrow Reco shared energy fraction", 51, 0, 1.02); - h_CP_simToRecoShEnF_Score_ = ibook.book2D("CP_simToRecoShEnF_Score", "CaloParticle #rightarrow PFCluster simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 51, 0, 1.02, 51, 0, 1.02); - std::string matching = doMatchByScore_ ? "_MatchByScore" : "_MatchByShEnF"; - std::string pfValidFolder = "HLT/ParticleFlow/PFClusterValidation"+matching+"_EnFracCut"+doubleToString(enFracCut_)+"_PtCut"+doubleToString(ptCut_); + std::string pfValidFolder = outFolder_ + "/PFClusterValidation" + matching + "_EnFracCut" + + doubleToString(enFracCut_) + "_PtCut" + doubleToString(ptCut_); ibook.setCurrentFolder(pfValidFolder); h_nSimClusters_ = ibook.book1D("nSimClusters", "Number of SimClusters;Number of SimClusters per event", 100, 0, 100); - h_nSimClustersPrimary_ = ibook.book1D("nSimClustersPrimary", "Number of Primary SimClusters;Number of Primary SimClusters per event", 100, 0, 100); + h_nSimClustersPrimary_ = ibook.book1D( + "nSimClustersPrimary", "Number of Primary SimClusters;Number of Primary SimClusters per event", 100, 0, 100); h_nPFClusters_ = ibook.book1D("nPFClusters", "Number of PFClusters per PFCandidate", 100, 0, 100); h_recoToSimScore_ = ibook.book1D("recoToSimScore", "recoToSimScore;Reco #rightarrow Sim score", 51, 0, 1.02); h_simToRecoScore_ = ibook.book1D("simToRecoScore", "simToRecoScore;Sim #rightarrow Reco score", 51, 0, 1.02); - h_simToRecoShEnF_ = ibook.book1D("simToRecoShEnF", "simToRecoSharedEnergy;Sim #rightarrow Reco shared energy fraction", 51, 0, 1.02); - h_simToRecoShEnF_Score_ = ibook.book2D("simToRecoShEnF_Score", "simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", 51, 0, 1.02, 51, 0, 1.02); - h_simToRecoShEnF_En_ = ibook.book2D("simToRecoShEnF_En", "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy", 51, 0, 1.02, 100, 0., 100.); - h_simToRecoShEnF_EnHits_ = ibook.book2D("simToRecoShEnF_EnHits", "simToRecoSharedEnergy vs Energy Hits;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", 51, 0, 1.02, 100, 0., 100.); - h_simToRecoShEnF_EnFrac_ = ibook.book2D("simToRecoShEnF_EnFrac", "simToRecoSharedEnergy vs Energy Fraction;Sim #rightarrow Reco shared energy fraction;EnFrac", 51, 0, 1.02, 220, 0., 1.1); - h_simToRecoShEnF_Mult_ = ibook.book2D("simToRecoShEnF_Mult", "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", 51, 0, 1.02, 200, 0., 200.); - h_simToRecoScore_En_ = ibook.book2D("simToRecoScore_En", "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy", 51, 0, 1.02, 100, 0., 100.); - h_simToRecoScore_EnHits_ = ibook.book2D("simToRecoScore_EnHits", "simToRecoScore vs Energy Hits;Sim #rightarrow Reco score;Energy_{hits}", 51, 0, 1.02, 100, 0., 100.); - h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", 51, 0, 1.02, 220, 0., 1.1); - h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 51, 0, 1.02, 200, 0., 200.); - h_SimTrackToSimHitsEnergyFraction_ = ibook.book1D("SimTrackToSimHitsEnergyFraction", "SimTrackToSimHitsEnergyFraction;SimTrack to SimHits energy fraction", 110, 0, 1.1); + h_simToRecoShEnF_ = + ibook.book1D("simToRecoShEnF", "simToRecoSharedEnergy;Sim #rightarrow Reco shared energy fraction", 51, 0, 1.02); + h_simToRecoShEnF_Score_ = + ibook.book2D("simToRecoShEnF_Score", + "simToRecoSharedEnergy_Score;Sim #rightarrow Reco shared energy fraction;Sim #rightarrow Reco score", + 51, + 0, + 1.02, + 51, + 0, + 1.02); + h_simToRecoShEnF_En_ = + ibook.book2D("simToRecoShEnF_En", + "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy", + 51, + 0, + 1.02, + 100, + 0., + 100.); + h_simToRecoShEnF_EnHits_ = + ibook.book2D("simToRecoShEnF_EnHits", + "simToRecoSharedEnergy vs Energy Hits;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", + 51, + 0, + 1.02, + 100, + 0., + 100.); + h_simToRecoShEnF_EnFrac_ = + ibook.book2D("simToRecoShEnF_EnFrac", + "simToRecoSharedEnergy vs Energy Fraction;Sim #rightarrow Reco shared energy fraction;EnFrac", + 51, + 0, + 1.02, + 220, + 0., + 1.1); + h_simToRecoShEnF_Mult_ = + ibook.book2D("simToRecoShEnF_Mult", + "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", + 51, + 0, + 1.02, + 200, + 0., + 200.); + h_simToRecoScore_En_ = ibook.book2D( + "simToRecoScore_En", "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy", 51, 0, 1.02, 100, 0., 100.); + h_simToRecoScore_EnHits_ = ibook.book2D("simToRecoScore_EnHits", + "simToRecoScore vs Energy Hits;Sim #rightarrow Reco score;Energy_{hits}", + 51, + 0, + 1.02, + 100, + 0., + 100.); + h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", + "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", + 51, + 0, + 1.02, + 220, + 0., + 1.1); + h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", + "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", + 51, + 0, + 1.02, + 200, + 0., + 200.); + h_SimTrackToSimHitsEnergyFraction_ = + ibook.book1D("SimTrackToSimHitsEnergyFraction", + "SimTrackToSimHitsEnergyFraction;SimTrack to SimHits energy fraction", + 110, + 0, + 1.1); for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); - h_nSimMatchedToOneReco_[ithr] = - ibook.book1D("nSimMatchedToOneReco", "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", 10, 0, 10); - h_nRecoMatchedToOneSim_[ithr] = - ibook.book1D("nRecoMatchedToOneSim", "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", 10, 0, 10); + h_nSimMatchedToOneReco_[ithr] = ibook.book1D( + "nSimMatchedToOneReco", + "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", + 10, + 0, + 10); + h_nRecoMatchedToOneSim_[ithr] = ibook.book1D( + "nRecoMatchedToOneSim", + "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", + 10, + 0, + 10); } - + for (auto& hVar : histoVarsSim) { auto [nBins, hMin, hMax] = hVar.second; @@ -324,7 +432,7 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev hMinY, hMaxY); - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); h2d_simClustersMatchedRecoClusters_[ithr][h2dVar.first] = @@ -382,7 +490,7 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev } } - ibook.setCurrentFolder("HLT/ParticleFlow/PFCandidates"); + ibook.setCurrentFolder(outFolder_ + "/PFCandidates"); h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); @@ -390,7 +498,7 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_PFCandPdgId_ = ibook.book1D("PFCandPdgId", "PFCandPdgId", 44, -22, 22); h_PFCandType_ = ibook.book1D("PFCandidateType", "PFCandidateType", 10, 0, 10); - ibook.setCurrentFolder("HLT/ParticleFlow/PFBlocks"); + ibook.setCurrentFolder(outFolder_ + "/PFBlocks"); h_NumElements_ = ibook.book1D("NumElements", "NumElements", 25, 0, 25); h_NumTrackElements_ = ibook.book1D("NumTrackElements", "NumTrackElements", 5, 0, 5); h_NumMuonElements_ = ibook.book1D("NumMuonElements", "NumMuonElements", 5, 0, 5); @@ -400,67 +508,67 @@ void PFTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::Ev h_NumHCALElements_ = ibook.book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); h_NumHGCALElements_ = ibook.book1D("NumHGCALElements", "NumHGCALElements", 5, 0, 5); - ibook.setCurrentFolder("HLT/ParticleFlow/PFTracks"); + ibook.setCurrentFolder(outFolder_ + "/PFTracks"); h_TrackCharge_ = ibook.book1D("TrackCharge", "TrackCharge", 5, -2, 2); h_TrackNumPoints_ = ibook.book1D("TrackNumPoints", "TrackNumPoints", 100, 0, 100); h_TrackNumMeasurements_ = ibook.book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); h_TrackImpactParameter_ = ibook.book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); - ibook.setCurrentFolder("HLT/ParticleFlow/PFClusters"); - h_PFClusterE_ = ibook.book1D("PFClusterE", "PFCluster Energy;E [GeV]", 100, 0, 100); - h_PFClusterEta_ = ibook.book1D("PFClusterEta", "PFCluster Eta;#eta", 120, -6, 6); - h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "PFCluster Phi;#phi", 128, -3.2, 3.2); - h_PFClusterDepth_ = ibook.book1D("PFClusterDepth", "PFCluster Depth;Depth", 10, 0, 10); - h_PFClusterNHits_ = ibook.book1D("PFClusterNHits", "PFCluster Number of Hits", 100, 0, 100); - h_PFClusterType_ = ibook.book1D("PFClusterEtaWidth", "PFCluster Eta Width;#sigma_{#eta}", 20, 0, 20); - h_PFClusterHitFraction_ = ibook.book1D("PFClusterHitFraction", "PFCluster Hit Fraction;Fraction", 100, 0.0, 1.1); + ibook.setCurrentFolder(outFolder_ + "/PFClusters"); + h_PFClusterE_ = ibook.book1D("PFClusterE", "RecoCluster Energy;E [GeV]", 100, 0, 100); + h_PFClusterEta_ = ibook.book1D("PFClusterEta", "RecoCluster Eta;#eta", 120, -6, 6); + h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "RecoCluster Phi;#phi", 128, -3.2, 3.2); + h_PFClusterDepth_ = ibook.book1D("PFClusterDepth", "RecoCluster Depth;Depth", 10, 0, 10); + h_PFClusterNHits_ = ibook.book1D("PFClusterNHits", "RecoCluster Number of Hits", 100, 0, 100); + h_PFClusterType_ = ibook.book1D("PFClusterEtaWidth", "RecoCluster Eta Width;#sigma_{#eta}", 20, 0, 20); + h_PFClusterHitFraction_ = ibook.book1D("PFClusterHitFraction", "RecoCluster Hit Fraction;Fraction", 100, 0.0, 1.1); h_PFClusterHitDetId_ = - ibook.book1D("PFClusterHitDetId", "PFCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); - + ibook.book1D("PFClusterHitDetId", "RecoCluster Hit DetId modulo 10000;DetId mod 10000", 100, 0, 10000); } -void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { +template +void PFTesterT::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { // -------------------------------------------------------------------- // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- - edm::Handle PFRechit; - iEvent.getByToken(PFRechitToken_, PFRechit); - if (!PFRechit.isValid()) { - edm::LogInfo("PFTester") << "Input PFRechit collection not found."; + edm::Handle Rechit; + iEvent.getByToken(RechitToken_, Rechit); + if (!Rechit.isValid()) { + edm::LogPrint("PFTester") << "Input Rechit collection not found."; return; } - auto pfRechit = *PFRechit; + auto pfRechit = *Rechit; - edm::Handle PFCluster; - iEvent.getByToken(PFClusterToken_, PFCluster); - if (!PFCluster.isValid()) { - edm::LogInfo("PFTester") << "Input PFCluster collection not found."; + edm::Handle RecoCluster; + iEvent.getByToken(RecoClusterToken_, RecoCluster); + if (!RecoCluster.isValid()) { + edm::LogPrint("PFTester") << "Input RecoCluster collection not found."; return; } - auto recoClusters = *PFCluster; + auto recoClusters = *RecoCluster; edm::Handle SimCluster; iEvent.getByToken(SimClusterToken_, SimCluster); if (!SimCluster.isValid()) { - edm::LogInfo("PFTester") << "Input SimCluster collection not found."; + edm::LogPrint("PFTester") << "Input SimCluster collection not found."; return; } auto simClusters = *SimCluster; - edm::Handle> SimToRecoAssociatorCollection; + edm::Handle> SimToRecoAssociatorCollection; iEvent.getByToken(SimToRecoAssociatorToken_, SimToRecoAssociatorCollection); if (!SimToRecoAssociatorCollection.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociator SimToReco collection not found."; + edm::LogPrint("PFTester") << "Input ClusterSimClusterAssociator SimToReco collection not found."; return; } auto simToRecoAssoc = *SimToRecoAssociatorCollection; - edm::Handle> RecoToSimAssociatorCollection; + edm::Handle> RecoToSimAssociatorCollection; iEvent.getByToken(RecoToSimAssociatorToken_, RecoToSimAssociatorCollection); if (!RecoToSimAssociatorCollection.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterSimClusterAssociator RecoToSim collection not found."; + edm::LogPrint("PFTester") << "Input ClusterSimClusterAssociator RecoToSim collection not found."; return; } auto recoToSimAssoc = *RecoToSimAssociatorCollection; @@ -472,23 +580,23 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) edm::Handle CaloParticle; iEvent.getByToken(CaloParticleToken_, CaloParticle); if (!CaloParticle.isValid()) { - edm::LogInfo("PFTester") << "Input CaloParticle collection not found."; + edm::LogPrint("PFTester") << "Input CaloParticle collection not found."; return; } auto caloParticles = *CaloParticle; - edm::Handle> CpToRecoAssociatorCollection; + edm::Handle> CpToRecoAssociatorCollection; iEvent.getByToken(CpToRecoAssociatorToken_, CpToRecoAssociatorCollection); if (!CpToRecoAssociatorCollection.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterCaloParticleAssociator SimToReco collection not found."; + edm::LogPrint("PFTester") << "Input ClusterCaloParticleAssociator SimToReco collection not found."; return; } auto cpToRecoAssoc = *CpToRecoAssociatorCollection; - edm::Handle> RecoToCpAssociatorCollection; + edm::Handle> RecoToCpAssociatorCollection; iEvent.getByToken(RecoToCpAssociatorToken_, RecoToCpAssociatorCollection); if (!RecoToCpAssociatorCollection.isValid()) { - edm::LogInfo("PFTester") << "Input PFClusterCaloParticleAssociator RecoToSim collection not found."; + edm::LogPrint("PFTester") << "Input ClusterCaloParticleAssociator RecoToSim collection not found."; return; } auto recoToCpAssoc = *RecoToCpAssociatorCollection; @@ -499,7 +607,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) std::unordered_map simClusterToCPEnergyMap; for (unsigned int cpId = 0; cpId < caloParticles.size(); ++cpId) { - // Fill map: for each simCluster, the energy of the caloParticle computed as the sum of all simClusters arising from it double energySumSimClusters = 0; double energySumSimHits = 0; @@ -516,8 +623,8 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters for (auto hit_fraction : sc.hits_and_fractions()) { DetId id(hit_fraction.first); - auto rechitIt = std::find_if(pfRechit.begin(), pfRechit.end(), - [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + auto rechitIt = + std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); if (rechitIt == pfRechit.end()) { continue; } else { @@ -528,15 +635,15 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) for (const auto& scRef : caloParticles[cpId].simClusters()) { simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; } - #ifdef debug - edm::LogPrint("PFTester") << " caloParticle [" << cpId << "]: energy=" << caloParticles[cpId].energy() - << ", energySumSimClusters=" << energySumSimClusters - << ", energySumSimHits=" << energySumSimHits - << ", energyFracSumSimHits=" << energyFracSumSimHits << std::endl; - #endif +#ifdef debug + edm::LogPrint("PFTester") << " caloParticle [" << cpId << "]: energy=" << caloParticles[cpId].energy() + << ", energySumSimClusters=" << energySumSimClusters + << ", energySumSimHits=" << energySumSimHits + << ", energyFracSumSimHits=" << energyFracSumSimHits << std::endl; +#endif - h_CaloParticleToSimClusterEnergyFraction_->Fill(energySumSimClusters/caloParticles[cpId].energy()); - h_CaloParticleToSimHitsEnergyFraction_->Fill(energySumSimHits/caloParticles[cpId].energy()); + h_CaloParticleToSimClusterEnergyFraction_->Fill(energySumSimClusters / caloParticles[cpId].energy()); + h_CaloParticleToSimHitsEnergyFraction_->Fill(energySumSimHits / caloParticles[cpId].energy()); // SimToReco association for caloParticles const edm::Ref caloParticleRef(CaloParticle, cpId); @@ -548,12 +655,12 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) continue; for (const auto& recoPair : cpToRecoMatched) { - #ifdef debug +#ifdef debug edm::LogPrint("PFTester") << " caloParticle [" << cpId << "] matched to RecoCluster [" << recoPair.first.index() - << "] with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits - << ", score: " << recoPair.second.second << std::endl; - #endif + << "] with shared energy: " << recoPair.second.first + << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", score: " << recoPair.second.second << std::endl; +#endif h_CP_simToRecoScore_->Fill(recoPair.second.second); h_CP_simToRecoShEnF_->Fill(recoPair.second.first / energyFracSumSimHits); h_CP_simToRecoShEnF_Score_->Fill(recoPair.second.first / energyFracSumSimHits, recoPair.second.second); @@ -562,7 +669,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // RecoToSim association for caloParticles for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { - const edm::Ref recoClusterRef(PFCluster, recoId); + const edm::Ref recoClusterRef(RecoCluster, recoId); const auto& recoToCpIt = recoToCpAssoc.find(recoClusterRef); if (recoToCpIt == recoToCpAssoc.end()) continue; @@ -577,11 +684,10 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // -------------------------------------------------------------------- // ----- Efficiency and split computation at cluster level ------------ // -------------------------------------------------------------------- - + uint nSimClusters = 0; uint nSimClustersPrimary = 0; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = 0; for (auto hit_energy : simClusters[simId].hits_and_energies()) { energySumSimHits += hit_energy.second; @@ -591,8 +697,8 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) double energyFracSumSimHits = 0; for (auto hit_energy : simClusters[simId].hits_and_fractions()) { DetId id(hit_energy.first); - auto rechitIt = std::find_if(pfRechit.begin(), pfRechit.end(), - [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + auto rechitIt = + std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); if (rechitIt == pfRechit.end()) { continue; } else { @@ -625,7 +731,7 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h_simClusters_["EnHits"]->Fill(energySumSimHits); h_simClusters_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClusters_["Pt"]->Fill(simClusters[simId].pt()); - h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); + h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); h_simClusters_["Eta"]->Fill(simTrackEtaAtBoundary); h_simClusters_["Phi"]->Fill(simClusters[simId].phi()); h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); @@ -652,12 +758,12 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) const auto& simToRecoMatched = simToRecoIt->val; if (simToRecoMatched.empty()) continue; - + h_simClustersReconstructable_["En"]->Fill(simClusters[simId].energy()); h_simClustersReconstructable_["EnHits"]->Fill(energySumSimHits); h_simClustersReconstructable_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); h_simClustersReconstructable_["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersReconstructable_["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersReconstructable_["PtLow"]->Fill(simClusters[simId].pt()); h_simClustersReconstructable_["Eta"]->Fill(simTrackEtaAtBoundary); h_simClustersReconstructable_["Phi"]->Fill(simClusters[simId].phi()); h_simClustersReconstructable_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); @@ -670,7 +776,8 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h2d_simClustersReconstructable_["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); h2d_simClustersReconstructable_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); h2d_simClustersReconstructable_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); - h2d_simClustersReconstructable_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClustersReconstructable_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, + simClusters[simId].numberOfRecHits()); h2d_simClustersReconstructable_["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClustersReconstructable_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClustersReconstructable_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); @@ -682,35 +789,37 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) unsigned nRecoMatchedToOneSim = 0; for (const auto& recoPair : simToRecoMatched) { - - #ifdef debug +#ifdef debug const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); auto ev = simClusters[simId].g4Tracks()[0].eventId().event(); auto bx = simClusters[simId].g4Tracks()[0].eventId().bunchCrossing(); - edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx << ", en=" << energySumSimHits << ", hits="; + edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx + << ", en=" << energySumSimHits << ", hits="; const auto& hits_fractions = simClusters[simId].hits_and_fractions(); - const auto& hits_energies = simClusters[simId].hits_and_energies(); + const auto& hits_energies = simClusters[simId].hits_and_energies(); auto itF = hits_fractions.begin(); auto itE = hits_energies.begin(); for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { DetId id(itF->first); const GlobalPoint pos = caloGeom.getPosition(id); - edm::LogPrint("PFTester") << " DetId=" << itF->first << ", eta=" << pos.eta() << ", phi=" << pos.phi() - << ", en=" << itE->second << ", fr=" << itF->second; + edm::LogPrint("PFTester") << " DetId=" << itF->first << ", eta=" << pos.eta() << ", phi=" << pos.phi() + << ", en=" << itE->second << ", fr=" << itF->second; } - edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" - << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits - << ", score: " << recoPair.second.second << ", hits="; + edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() + << "], en=" << recoClusters[recoPair.first.index()].energy() + << ", with shared energy: " << recoPair.second.first + << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", score: " << recoPair.second.second << ", hits="; for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { DetId id(hit_energy.recHitRef()->detId()); const GlobalPoint pos = caloGeom.getPosition(id); - edm:: LogPrint("PFTester") << " DetId=" << hit_energy.recHitRef()->detId() << ", eta=" << pos.eta() << ", phi=" << pos.phi() - << ", en=" << hit_energy.recHitRef()->energy() << ", fr=" << hit_energy.fraction(); + edm::LogPrint("PFTester") << " DetId=" << hit_energy.recHitRef()->detId() << ", eta=" << pos.eta() + << ", phi=" << pos.phi() << ", en=" << hit_energy.recHitRef()->energy() + << ", fr=" << hit_energy.fraction(); } - #endif +#endif auto score = recoPair.second.second; auto shared_energy = recoPair.second.first; @@ -739,7 +848,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) nRecoMatchedToOneSim++; } } - } // efficiency numerator @@ -754,19 +862,28 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), + simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), + simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Mult"]->Fill(energySumSimHits, + simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, + simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, + simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, + simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), + simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), + simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), + simClusters[simId].phi()); // split numerator if (nRecoMatchedToOneSim > 1) { @@ -782,24 +899,22 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } h_nRecoMatchedToOneSim_[ithr]->Fill(nRecoMatchedToOneSim); - } } h_nSimClusters_->Fill(nSimClusters); h_nSimClustersPrimary_->Fill(nSimClustersPrimary); - + // -------------------------------------------------------------------- // ----- Fakes and merge computation at cluster level ----------------- // -------------------------------------------------------------------- h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { - // fake and merge denominator h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); - h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); + // h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); + // h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); @@ -807,13 +922,13 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); - h2d_recoClusters_["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); - h2d_recoClusters_["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); - h2d_recoClusters_["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); + // h2d_recoClusters_["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); + // h2d_recoClusters_["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); + // h2d_recoClusters_["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); h2d_recoClusters_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClusters_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - const edm::Ref recoClusterRef(PFCluster, recoId); + const edm::Ref recoClusterRef(RecoCluster, recoId); const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); if (recoToSimIt == recoToSimAssoc.end()) continue; @@ -829,10 +944,11 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) for (const auto& simPair : recoToSimMatched) { const auto simPairIdx = simPair.first.index(); - #ifdef debug - edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx - << " score = " << simPair.second << std::endl; - #endif +#ifdef debug + edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId + << " : matched simCluster id = " << simPairIdx << " score = " << simPair.second + << std::endl; +#endif double energySumSimHits = 0; for (auto hit_energy : simClusters[simPairIdx].hits_and_energies()) { @@ -865,26 +981,34 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // fake numerator if (nSimMatchedToOneReco > 0) { h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); + // h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + // h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); - h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].size()); + // h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Eta"]->Fill(recoClusters[recoId].pt(), + // recoClusters[recoId].eta()); + // h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Phi"]->Fill(recoClusters[recoId].pt(), + // recoClusters[recoId].phi()); + // h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Mult"]->Fill(recoClusters[recoId].pt(), + // recoClusters[recoId].size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), + recoClusters[recoId].eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), + recoClusters[recoId].phi()); // merge numerator if (nSimMatchedToOneReco > 1) { h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); - h_recoClustersMultiMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); + // h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); + // h_recoClustersMultiMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); @@ -892,7 +1016,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } h_nSimMatchedToOneReco_[ithr]->Fill(nSimMatchedToOneReco); - } } @@ -901,7 +1024,6 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // -------------------------------------------------------------------- for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = 0; for (auto hit_energy : simClusters[simId].hits_and_energies()) { energySumSimHits += hit_energy.second; @@ -932,8 +1054,9 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) // they should already be sorted by score std::vector simToRecoMatchedSorted(simToRecoMatched.begin(), simToRecoMatched.end()); - std::sort(simToRecoMatchedSorted.begin(), simToRecoMatchedSorted.end(), - [](const auto& a, const auto& b) { return a.second.second < b.second.second; }); + std::sort(simToRecoMatchedSorted.begin(), simToRecoMatchedSorted.end(), [](const auto& a, const auto& b) { + return a.second.second < b.second.second; + }); for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; @@ -954,21 +1077,30 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } if (passMatch) { - h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].pt() / simClusters[simId].pt()); - - h2d_responseE_[ithr]["En"]->Fill(simClusters[simId].energy(), recoClusters[recoId].energy() / energySumSimHits); + // h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), + // recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, + // recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), + // recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, + // recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["Phi"]->Fill(simClusters[simId].phi(), + // recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), + // recoClusters[recoId].pt() / simClusters[simId].pt()); + + h2d_responseE_[ithr]["En"]->Fill(simClusters[simId].energy(), + recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, + recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), + recoClusters[recoId].energy() / energySumSimHits); break; } } @@ -983,13 +1115,13 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) edm::Handle PFCand; iEvent.getByToken(PFCandToken_, PFCand); if (!PFCand.isValid()) { - edm::LogInfo("PFTester") << "Input PFCand collection not found."; + edm::LogPrint("PFTester") << "Input PFCand collection not found."; return; } pf_candidates = PFCand.product(); if (!pf_candidates) { - edm::LogInfo("PFTester") << " Failed to retrieve data required by PFTester.cc"; + edm::LogPrint("PFTester") << " Failed to retrieve data required by PFTester.cc"; return; } @@ -1094,7 +1226,8 @@ void PFTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) } // convert a double to the string format -std::string PFTester::doubleToString(double x) const { +template +std::string PFTesterT::doubleToString(double x) const { std::ostringstream result; result << std::setprecision(2) << x; @@ -1108,4 +1241,8 @@ std::string PFTester::doubleToString(double x) const { return xnew; } -DEFINE_FWK_MODULE(PFTester); +using PFClusterTester = PFTesterT; +using CaloClusterTester = PFTesterT; + +DEFINE_FWK_MODULE(PFClusterTester); +DEFINE_FWK_MODULE(CaloClusterTester); diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index ad5c0dd42b78d..4227e3a9c86f9 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -1,12 +1,12 @@ import FWCore.ParameterSet.Config as cms from DQMServices.Core.DQMEDHarvester import DQMEDHarvester -from Validation.RecoParticleFlow.hltPFValidation_cfi import hltPFTesterECAL -_thresholds = [str(x).replace('.', 'p') for x in hltPFTesterECAL.assocScoreThresholds] +from Validation.RecoParticleFlow.hltPFValidation_cfi import hltPFClusterTesterECAL +_thresholds = [str(x).replace('.', 'p') for x in hltPFClusterTesterECAL.assocScoreThresholds] hltPFClusterPostProcessor = DQMEDHarvester( "DQMGenericClient", - subDirs=cms.untracked.vstring("HLT/ParticleFlow/PFClusterValidation*"), + subDirs=cms.untracked.vstring("HLT/*"), efficiency = cms.vstring( *[ item for thr in _thresholds @@ -31,9 +31,9 @@ *[ item for thr in _thresholds for item in ( - f"'Score{thr}/Fake_vs_EnEta' 'Fake Rate vs Energy-#eta' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClustersPt_Eta fake", - f"'Score{thr}/Fake_vs_EnPhi' 'Fake Rate vs Energy-#phi' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClustersPt_Phi fake", - f"'Score{thr}/Fake_vs_EnMult' 'Fake Rate vs Energy-Mult' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClustersPt_Mult fake", + f"'Score{thr}/Fake_vs_EnEta' 'Fake Rate vs Energy-#eta' Score{thr}/RecoClustersMatchedSimClustersEn_Eta RecoClustersEn_Eta fake", + f"'Score{thr}/Fake_vs_EnPhi' 'Fake Rate vs Energy-#phi' Score{thr}/RecoClustersMatchedSimClustersEn_Phi RecoClustersEn_Phi fake", + f"'Score{thr}/Fake_vs_EnMult' 'Fake Rate vs Energy-Mult' Score{thr}/RecoClustersMatchedSimClustersEn_Mult RecoClustersEn_Mult fake", f"'Score{thr}/Fake_vs_PtEta' 'Fake Rate vs p_{{T}}-#eta' Score{thr}/RecoClustersMatchedSimClustersPt_Eta RecoClustersPt_Eta fake", f"'Score{thr}/Fake_vs_PtPhi' 'Fake Rate vs p_{{T}}-#phi' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClustersPt_Phi fake", f"'Score{thr}/Fake_vs_PtMult' 'Fake Rate vs p_{{T}}-Mult' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClustersPt_Mult fake", diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index a86c245d65bf9..740ca4fdff83c 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -1,5 +1,4 @@ import FWCore.ParameterSet.Config as cms -from Validation.Configuration.hltHGCalSimValid_cff import hltRecHitMapProducer hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), @@ -25,69 +24,70 @@ label_cp = cms.InputTag("mix","MergedCaloTruth") ) -hltPFTesterECAL = cms.EDProducer("PFTester", +hltPFClusterTesterECAL = cms.EDProducer("PFClusterTester", PFCand = cms.InputTag("hltParticleFlow"), - PFRechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), - PFCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + Rechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), + RecoCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), SimCluster = cms.InputTag("mix","MergedCaloTruth"), - PFClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), - PFClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + ClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), + ClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + outFolder = cms.string('HLT/ParticleFlow'), assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.) ) -hltPFTesterECALWithCut1 = hltPFTesterECAL.clone( +from Configuration.Eras.Modifier_phase2_common_cff import phase2_common +phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp")) + +hltPFClusterTesterECALWithCut1 = hltPFClusterTesterECAL.clone( enFracCut = cms.double(0.01), ptCut = cms.double(0.) ) -hltPFTesterECALWithCut2 = hltPFTesterECAL.clone( +hltPFClusterTesterECALWithCut2 = hltPFClusterTesterECAL.clone( enFracCut = cms.double(0.), ptCut = cms.double(0.1) ) -hltPFTesterECALWithCut3 = hltPFTesterECAL.clone( +hltPFClusterTesterECALWithCut3 = hltPFClusterTesterECAL.clone( enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) # SimToReco match based on shared energy fraction -hltPFTesterECALShEnF = hltPFTesterECAL.clone( +hltPFClusterTesterECALShEnF = hltPFClusterTesterECAL.clone( doMatchByScore = cms.bool(False) ) -hltPFTesterECALShEnFWithCut1 = hltPFTesterECALShEnF.clone( +hltPFClusterTesterECALShEnFWithCut1 = hltPFClusterTesterECALShEnF.clone( enFracCut = cms.double(0.01), ptCut = cms.double(0.) ) -hltPFTesterECALShEnFWithCut2 = hltPFTesterECALShEnF.clone( +hltPFClusterTesterECALShEnFWithCut2 = hltPFClusterTesterECALShEnF.clone( enFracCut = cms.double(0.), ptCut = cms.double(0.1) ) -hltPFTesterECALShEnFWithCut3 = hltPFTesterECALShEnF.clone( +hltPFClusterTesterECALShEnFWithCut3 = hltPFClusterTesterECALShEnF.clone( enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) -from Configuration.Eras.Modifier_phase2_common_cff import phase2_common -phase2_common.toModify(hltPFTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp")) - PFValSeq = cms.Sequence( hltPFScAssocByEnergyScoreProducer +hltPFClusterSimClusterAssociationProducerECAL +hltPFCpAssocByEnergyScoreProducer +hltPFClusterCaloParticleAssociationProducerECAL - +hltPFTesterECAL - +hltPFTesterECALWithCut1 - +hltPFTesterECALWithCut2 - +hltPFTesterECALWithCut3 - +hltPFTesterECALShEnF - +hltPFTesterECALShEnFWithCut1 - +hltPFTesterECALShEnFWithCut2 - +hltPFTesterECALShEnFWithCut3 + +hltPFClusterTesterECAL + +hltPFClusterTesterECALWithCut1 + +hltPFClusterTesterECALWithCut2 + +hltPFClusterTesterECALWithCut3 + +hltPFClusterTesterECALShEnF + +hltPFClusterTesterECALShEnFWithCut1 + +hltPFClusterTesterECALShEnFWithCut2 + +hltPFClusterTesterECALShEnFWithCut3 ) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 7642e0c8dec72..2e57e76513fea 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -245,7 +245,7 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): plotter.limits(y=(0,1.1), x=(bin_edges[0], bin_edges[-1])) else: if "Response" in name: - plotter.limits(y=(0, 1.5)) + plotter.limits(y=(0, 2.0)) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) @@ -481,6 +481,7 @@ def __call__(self, parser, namespace, values, option_string=None): parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') parser.add_argument('--PtCut', default=0.01, help='Cut on the sim cluster energy fraction.') parser.add_argument('--match_by_score', default=1, type=int, help='Use association based on score (if false, use shared energy fraction).') + parser.add_argument('--ticl', default=False, action='store_true', help='Use TiclBarrel folder.') mutual_excl2 = parser.add_mutually_exclusive_group(required=True) mutual_excl2.add_argument('-f', '--file', help='Paths to the DQM ROOT file.') @@ -505,9 +506,12 @@ def __call__(self, parser, namespace, values, option_string=None): nSimClustersLabel = '# SimClusters' nPFClustersLabel = '# PFClusters' - titles = {'response': r"$p_{T}^{Reco}/p_{T}^{Sim}$", - 'av_response': r"$$", - 'resolution': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", + titles = {'responsePt': r"$p_{T}^{Reco}/p_{T}^{Sim}$", + 'response': r"$E^{Reco}/E^{Sim}$", + 'av_responsePt': r"$$", + 'av_response': r"$$", + 'resolutionPt': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", + 'resolution': r"$\sigma(E^{Reco}/E^{Sim}) / $", 'eff': 'Efficiency', 'fake': 'Fake Rate', 'split': 'Split Rate', @@ -518,7 +522,9 @@ def __call__(self, parser, namespace, values, option_string=None): else: matching = '_MatchByShEnF' print("### INFO: Using association by shared energy fraction.") - dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/PFClusterValidation{matching}_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" + if args.ticl: sub_folder = 'TiclBarrel' + else: sub_folder = 'ParticleFlow' + dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/PFClusterValidation{matching}_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) @@ -761,7 +767,9 @@ def __call__(self, parser, namespace, values, option_string=None): # Temporary hack to access CaloParticle histrograms ################################################################################## - dqm_dir = f"DQMData/Run 1/HLT/Run summary/ParticleFlow/CaloParticles_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" + if args.ticl: sub_folder = 'TiclBarrel' + else: sub_folder = 'ParticleFlow' + dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/CaloParticles_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) From 9e37336dc5a3277aecd570070748a2f2cf52c884 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Tue, 9 Dec 2025 16:01:34 +0100 Subject: [PATCH 14/64] Adapt RecHitMapProducer and inheritances to RefProd logic --- .../ParticleFlowReco/src/classes_def_2.xml | 2 +- .../plugins/RecHitMapProducer.cc | 38 ++++++++++--------- .../python/recHitMapProducer_cff.py | 2 +- .../python/hltBarrelSimValid_cff.py | 3 +- .../python/hltHGCalSimValid_cff.py | 7 ++-- .../python/hltPFValidation_cfi.py | 4 +- 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/DataFormats/ParticleFlowReco/src/classes_def_2.xml b/DataFormats/ParticleFlowReco/src/classes_def_2.xml index 8951eddabfb17..9b21aebde986b 100644 --- a/DataFormats/ParticleFlowReco/src/classes_def_2.xml +++ b/DataFormats/ParticleFlowReco/src/classes_def_2.xml @@ -69,7 +69,7 @@ - + diff --git a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc index a77b7996b7d18..0958bff8f1a28 100644 --- a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc +++ b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc @@ -13,6 +13,7 @@ #include "DataFormats/HGCRecHit/interface/HGCRecHitCollections.h" #include "DataFormats/ParticleFlowReco/interface/PFRecHit.h" +#include "DataFormats/ParticleFlowReco/interface/PFRecHitFwd.h" #include "DataFormats/Common/interface/RefProdVector.h" #include "DataFormats/Common/interface/MultiSpan.h" @@ -27,15 +28,15 @@ class RecHitMapProducer : public edm::global::EDProducer<> { std::vector> hgcal_hits_token_; std::vector> barrel_hits_token_; - bool hgcalOnly_; - bool barrelOnly_; + bool doHgcal_; + bool doBarrel_; }; DEFINE_FWK_MODULE(RecHitMapProducer); using DetIdRecHitMap = std::unordered_map; -RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : hgcalOnly_(ps.getParameter("hgcalOnly")), barrelOnly_(ps.getParameter("barrelOnly")) { +RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : doHgcal_(ps.getParameter("doHgcal")), doBarrel_(ps.getParameter("doBarrel")) { std::vector tags = ps.getParameter>("hits"); for (auto& tag : tags) { if (tag.label().find("HGCalRecHit") != std::string::npos) { @@ -44,16 +45,10 @@ RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : hgcalOnly_(p barrel_hits_token_.push_back(consumes(tag)); } } - - if (!barrelOnly_) { - produces>("RefProdVectorHGCRecHitCollection"); - produces("hgcalRecHitMap"); - } - - if (!hgcalOnly_) { - produces>("RefProdVectorPFRecHitCollection"); - produces("barrelRecHitMap"); - } + produces>("RefProdVectorHGCRecHitCollection"); + produces("hgcalRecHitMap"); + produces>("RefProdVectorPFRecHitCollection"); + produces("barrelRecHitMap"); } void RecHitMapProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { @@ -62,13 +57,14 @@ void RecHitMapProducer::fillDescriptions(edm::ConfigurationDescriptions& descrip {edm::InputTag("HGCalRecHit", "HGCEERecHits"), edm::InputTag("HGCalRecHit", "HGCHEFRecHits"), edm::InputTag("HGCalRecHit", "HGCHEBRecHits")}); - desc.add("hgcalOnly", true); - desc.add("barrelOnly", false); + desc.add("doHgcal", true); + desc.add("doBarrel", false); descriptions.add("recHitMapProducer", desc); } void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::EventSetup& es) const { - if (!barrelOnly_) { + + if (doHgcal_) { auto hitMapHGCal = std::make_unique(); // Retrieve collections @@ -99,9 +95,12 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event evt.put(std::move(mcHGCRecHit), "RefProdVectorHGCRecHitCollection"); evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); } + } else { + evt.put(std::make_unique>(), "RefProdVectorHGCRecHitCollection"); + evt.put(std::make_unique(), "hgcalRecHitMap"); } - if (!hgcalOnly_) { + if (doBarrel_) { auto hitMapBarrel = std::make_unique(); assert(barrel_hits_token_.size() == 2); @@ -111,7 +110,7 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event if (!ecal_hits.isValid() || !hbhe_hits.isValid()) { edm::LogWarning("HGCalRecHitMapProducer") << "One or more barrel hit collections are unavailable. Returning an empty map."; - evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); + evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); } else { // Fix order by storing a edm::RefProdVector @@ -128,5 +127,8 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event evt.put(std::move(mcPFRecHit), "RefProdVectorPFRecHitCollection"); evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); } + } else { + evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); + evt.put(std::make_unique(), "barrelRecHitMap"); } } diff --git a/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py b/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py index dd594012d706f..5de1a6ec794a1 100644 --- a/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py +++ b/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py @@ -11,4 +11,4 @@ "particleFlowRecHitHBHE"] from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel -ticl_barrel.toModify(recHitMapProducer, hits = hits, hgcalOnly = False) +ticl_barrel.toModify(recHitMapProducer, hits = hits, doHgcal = True, doBarrel = True) diff --git a/Validation/Configuration/python/hltBarrelSimValid_cff.py b/Validation/Configuration/python/hltBarrelSimValid_cff.py index 0bd7f290ae848..755ef43785d1e 100644 --- a/Validation/Configuration/python/hltBarrelSimValid_cff.py +++ b/Validation/Configuration/python/hltBarrelSimValid_cff.py @@ -4,7 +4,8 @@ barrel_hits = ["hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"] hltBarrelRecHitMapProducer = _recHitMapProducer.clone( hits = barrel_hits, - hgcalOnly = False, + doHgcal = False, + doBarrel = True, ) from SimCalorimetry.HGCalAssociatorProducers.hltLCToCPAssociation_cfi import (hltBarrelLCToCPAssociatorByEnergyScoreProducer, diff --git a/Validation/Configuration/python/hltHGCalSimValid_cff.py b/Validation/Configuration/python/hltHGCalSimValid_cff.py index d7822a28eda90..873a2871984ac 100644 --- a/Validation/Configuration/python/hltHGCalSimValid_cff.py +++ b/Validation/Configuration/python/hltHGCalSimValid_cff.py @@ -25,7 +25,8 @@ hltHGCalRecHitMapProducer = _hltBarrelRecHitMapProducer.clone( hits = hgcal_hits, - hgcalOnly = True, + doHgcal = True, + doBarrel = False, ) from Configuration.Eras.Modifier_phase2_common_cff import phase2_common from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel @@ -67,12 +68,12 @@ # LC to CP and LC to SC associators TICL-based for barrel region (ticl_barrel) hltBarrelLcAssocByEnergyScoreProducer = _barrelLcAssocByEnergyScoreProducer.clone( - hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), hitMapTag = cms.InputTag("hltRecHitMapProducer","barrelRecHitMap"), ) hltBarrelScAssocByEnergyScoreProducer = _barrelScAssocByEnergyScoreProducer.clone( - hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), hitMapTag = cms.InputTag("hltRecHitMapProducer","barrelRecHitMap"), ) diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 740ca4fdff83c..af5bacedcfb42 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -3,7 +3,7 @@ hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), - hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), # hltParticleFlowClusterHO + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), ) hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", @@ -15,7 +15,7 @@ hltPFCpAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToCPAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), - hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), # hltParticleFlowClusterHO + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), ) hltPFClusterCaloParticleAssociationProducerECAL = cms.EDProducer("PCToCPAssociatorEDProducer", From fa5431af35603d45bb31f3170807b4b10a61d06a Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 17 Dec 2025 15:39:57 +0100 Subject: [PATCH 15/64] Make recHitMapProducer labels compatible with Run3 geometry --- .../plugins/RecHitMapProducer.cc | 61 +++++++++++-------- .../python/recHitMapProducer_cff.py | 2 +- .../LCToCPAssociatorByEnergyScoreProducer.cc | 2 +- .../LCToSCAssociatorByEnergyScoreProducer.cc | 2 +- .../python/hltBarrelSimValid_cff.py | 4 +- .../python/hltHGCalSimValid_cff.py | 10 +-- .../python/hltPFValidation_cfi.py | 4 +- 7 files changed, 49 insertions(+), 36 deletions(-) diff --git a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc index 0958bff8f1a28..02539669e3b3c 100644 --- a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc +++ b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc @@ -25,30 +25,35 @@ class RecHitMapProducer : public edm::global::EDProducer<> { void produce(edm::StreamID, edm::Event&, const edm::EventSetup&) const override; private: + std::vector hgcal_hits_tag_; + std::vector pf_hits_tag_; std::vector> hgcal_hits_token_; - std::vector> barrel_hits_token_; + std::vector> pf_hits_token_; - bool doHgcal_; - bool doBarrel_; + bool doHgcalHits_; + bool doPFHits_; }; DEFINE_FWK_MODULE(RecHitMapProducer); using DetIdRecHitMap = std::unordered_map; -RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) : doHgcal_(ps.getParameter("doHgcal")), doBarrel_(ps.getParameter("doBarrel")) { +RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) + : doHgcalHits_(ps.getParameter("doHgcalHits")), doPFHits_(ps.getParameter("doPFHits")) { std::vector tags = ps.getParameter>("hits"); for (auto& tag : tags) { if (tag.label().find("HGCalRecHit") != std::string::npos) { + hgcal_hits_tag_.push_back(tag); hgcal_hits_token_.push_back(consumes(tag)); } else { - barrel_hits_token_.push_back(consumes(tag)); + pf_hits_tag_.push_back(tag); + pf_hits_token_.push_back(consumes(tag)); } } produces>("RefProdVectorHGCRecHitCollection"); produces("hgcalRecHitMap"); produces>("RefProdVectorPFRecHitCollection"); - produces("barrelRecHitMap"); + produces("pfRecHitMap"); } void RecHitMapProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { @@ -57,14 +62,14 @@ void RecHitMapProducer::fillDescriptions(edm::ConfigurationDescriptions& descrip {edm::InputTag("HGCalRecHit", "HGCEERecHits"), edm::InputTag("HGCalRecHit", "HGCHEFRecHits"), edm::InputTag("HGCalRecHit", "HGCHEBRecHits")}); - desc.add("doHgcal", true); - desc.add("doBarrel", false); + desc.add("doHgcalHits", true); + desc.add("doPFHits", false); descriptions.add("recHitMapProducer", desc); } void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::EventSetup& es) const { - if (doHgcal_) { + if (doHgcalHits_) { auto hitMapHGCal = std::make_unique(); // Retrieve collections @@ -75,8 +80,11 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event // Check validity of all handles if (!ee_hits.isValid() || !fh_hits.isValid() || !bh_hits.isValid()) { - edm::LogWarning("HGCalRecHitMapProducer") << "One or more HGCal hit collections are unavailable. Returning an " - "empty map and an empty RefProdVectorHGCRecHitCollection"; + edm::LogWarning("HGCalRecHitMapProducer") << "One or more of the following HGCal hit collections are unavailable: "; + for (auto& tag : hgcal_hits_tag_) { + edm::LogWarning("HGCalRecHitMapProducer") << " - " << tag; + } + edm::LogWarning("HGCalRecHitMapProducer") << "Returning an empty map and an empty RefProdVectorHGCRecHitCollection"; evt.put(std::make_unique>(), "RefProdVectorHGCRecHitCollection"); evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); } else { @@ -100,35 +108,38 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event evt.put(std::make_unique(), "hgcalRecHitMap"); } - if (doBarrel_) { - auto hitMapBarrel = std::make_unique(); + if (doPFHits_) { + auto hitMapPF = std::make_unique(); - assert(barrel_hits_token_.size() == 2); - const auto& ecal_hits = evt.getHandle(barrel_hits_token_[0]); - const auto& hbhe_hits = evt.getHandle(barrel_hits_token_[1]); + assert(pf_hits_token_.size() == 2); + const auto& ecal_hits = evt.getHandle(pf_hits_token_[0]); + const auto& hbhe_hits = evt.getHandle(pf_hits_token_[1]); if (!ecal_hits.isValid() || !hbhe_hits.isValid()) { - edm::LogWarning("HGCalRecHitMapProducer") - << "One or more barrel hit collections are unavailable. Returning an empty map."; + edm::LogWarning("HGCalRecHitMapProducer") << "One or more of the following PF hit collections are unavailable: "; + for (auto& tag : pf_hits_tag_) { + edm::LogWarning("HGCalRecHitMapProducer") << " - " << tag; + } + edm::LogWarning("HGCalRecHitMapProducer") << "Returning an empty map."; evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); - evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); + evt.put(std::move(hitMapPF), "pfRecHitMap"); } else { // Fix order by storing a edm::RefProdVector auto mcPFRecHit = std::make_unique>(); mcPFRecHit->push_back(edm::RefProd(ecal_hits)); mcPFRecHit->push_back(edm::RefProd(hbhe_hits)); - edm::MultiSpan barrelRechitSpan(*mcPFRecHit); - for (unsigned int i = 0; i < barrelRechitSpan.size(); ++i) { - const auto recHitDetId = barrelRechitSpan[i].detId(); - hitMapBarrel->emplace(recHitDetId, i); + edm::MultiSpan pfRechitSpan(*mcPFRecHit); + for (unsigned int i = 0; i < pfRechitSpan.size(); ++i) { + const auto recHitDetId = pfRechitSpan[i].detId(); + hitMapPF->emplace(recHitDetId, i); } evt.put(std::move(mcPFRecHit), "RefProdVectorPFRecHitCollection"); - evt.put(std::move(hitMapBarrel), "barrelRecHitMap"); + evt.put(std::move(hitMapPF), "pfRecHitMap"); } } else { evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); - evt.put(std::make_unique(), "barrelRecHitMap"); + evt.put(std::make_unique(), "pfRecHitMap"); } } diff --git a/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py b/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py index 5de1a6ec794a1..1509708b11913 100644 --- a/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py +++ b/RecoLocalCalo/HGCalRecProducers/python/recHitMapProducer_cff.py @@ -11,4 +11,4 @@ "particleFlowRecHitHBHE"] from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel -ticl_barrel.toModify(recHitMapProducer, hits = hits, doHgcal = True, doBarrel = True) +ticl_barrel.toModify(recHitMapProducer, hits = hits, doHgcalHits = True, doPFHits = True) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreProducer.cc index 7b14b1470943f..10ca68fd75b1a 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreProducer.cc @@ -75,7 +75,7 @@ void LCToCPAssociatorByEnergyScoreProducerT::fillDescriptions(edm: desc.add("hitMapTag", edm::InputTag("recHitMapProducer", "hgcalRecHitMap")); desc.add("hits", edm::InputTag("recHitMapProducer", "RefProdVectorHGCRecHitCollection")); } else { - desc.add("hitMapTag", edm::InputTag("recHitMapProducer", "barrelRecHitMap")); + desc.add("hitMapTag", edm::InputTag("recHitMapProducer", "pfRecHitMap")); desc.add("hits", edm::InputTag("recHitMapProducer", "RefProdVectorPFRecHitCollection")); } cfg.addWithDefaultLabel(desc); diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreProducer.cc index a5387e794630d..cc2db1452fdd0 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreProducer.cc @@ -74,7 +74,7 @@ void LCToSCAssociatorByEnergyScoreProducerT::fillDescriptions(edm: desc.add("hitMapTag", edm::InputTag("recHitMapProducer", "hgcalRecHitMap")); desc.add("hits", edm::InputTag("recHitMapProducer", "RefProdVectorHGCRecHitCollection")); } else { - desc.add("hitMapTag", edm::InputTag("recHitMapProducer", "barrelRecHitMap")); + desc.add("hitMapTag", edm::InputTag("recHitMapProducer", "pfRecHitMap")); desc.add("hits", edm::InputTag("recHitMapProducer", "RefProdVectorPFRecHitCollection")); } cfg.addWithDefaultLabel(desc); diff --git a/Validation/Configuration/python/hltBarrelSimValid_cff.py b/Validation/Configuration/python/hltBarrelSimValid_cff.py index 755ef43785d1e..0c8d9d7435cdf 100644 --- a/Validation/Configuration/python/hltBarrelSimValid_cff.py +++ b/Validation/Configuration/python/hltBarrelSimValid_cff.py @@ -4,8 +4,8 @@ barrel_hits = ["hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"] hltBarrelRecHitMapProducer = _recHitMapProducer.clone( hits = barrel_hits, - doHgcal = False, - doBarrel = True, + doHgcalHits = False, + doPFHits = True, ) from SimCalorimetry.HGCalAssociatorProducers.hltLCToCPAssociation_cfi import (hltBarrelLCToCPAssociatorByEnergyScoreProducer, diff --git a/Validation/Configuration/python/hltHGCalSimValid_cff.py b/Validation/Configuration/python/hltHGCalSimValid_cff.py index 873a2871984ac..6440afc962ca7 100644 --- a/Validation/Configuration/python/hltHGCalSimValid_cff.py +++ b/Validation/Configuration/python/hltHGCalSimValid_cff.py @@ -25,8 +25,8 @@ hltHGCalRecHitMapProducer = _hltBarrelRecHitMapProducer.clone( hits = hgcal_hits, - doHgcal = True, - doBarrel = False, + doHgcalHits = True, + doPFHits = False, ) from Configuration.Eras.Modifier_phase2_common_cff import phase2_common from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel @@ -34,6 +34,8 @@ (phase2_common & ticl_barrel).toModify(hltRecHitMapProducer, hits = [*hgcal_hits, *barrel_hits], + doHgcalHits = True, + doPFHits = True, ) # LC to CP and LC to SC associators TICL-based for HGCal region @@ -69,12 +71,12 @@ hltBarrelLcAssocByEnergyScoreProducer = _barrelLcAssocByEnergyScoreProducer.clone( hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), - hitMapTag = cms.InputTag("hltRecHitMapProducer","barrelRecHitMap"), + hitMapTag = cms.InputTag("hltRecHitMapProducer","pfRecHitMap"), ) hltBarrelScAssocByEnergyScoreProducer = _barrelScAssocByEnergyScoreProducer.clone( hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), - hitMapTag = cms.InputTag("hltRecHitMapProducer","barrelRecHitMap"), + hitMapTag = cms.InputTag("hltRecHitMapProducer","pfRecHitMap"), ) hltBarrelLayerClusterCaloParticleAssociationProducer = _barrelLayerClusterCaloParticleAssociation.clone( diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index af5bacedcfb42..498caa93fc58c 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -2,7 +2,7 @@ hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), - hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), + hitMapTag = cms.InputTag("hltRecHitMapProducer:pfRecHitMap"), hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), ) @@ -14,7 +14,7 @@ hltPFCpAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToCPAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), - hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), + hitMapTag = cms.InputTag("hltRecHitMapProducer:pfRecHitMap"), hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), ) From f5d4d4e8d58fa33c6d8e9dcdb4c74da0dedd382a Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 10 Dec 2025 13:34:30 +0100 Subject: [PATCH 16/64] Remove reconstructable, only use one Pt EnFracCut as default, rename energy variable --- .../python/HLTHGCalValidator_cff.py | 33 +- .../RecoParticleFlow/plugins/PFTester.cc | 334 +++++++----------- .../python/hltPFPostProcessor_cfi.py | 60 ++-- .../python/hltPFValidation_cfi.py | 34 +- .../scripts/makeHLTPFValidationPlots.py | 183 +++++----- 5 files changed, 255 insertions(+), 389 deletions(-) diff --git a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py index f3497a3960036..349c6fd6f9a57 100644 --- a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py +++ b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py @@ -62,18 +62,8 @@ ptCut = cms.double(0.) ) -hltLayerClusterTesterECALWithCut1 = hltLayerClusterTesterECAL.clone( +hltLayerClusterTesterECALWithCut = hltLayerClusterTesterECAL.clone( enFracCut = cms.double(0.01), - ptCut = cms.double(0.) -) - -hltLayerClusterTesterECALWithCut2 = hltLayerClusterTesterECAL.clone( - enFracCut = cms.double(0.), - ptCut = cms.double(0.1) -) - -hltLayerClusterTesterECALWithCut3 = hltLayerClusterTesterECAL.clone( - enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) @@ -82,34 +72,19 @@ doMatchByScore = cms.bool(False) ) -hltLayerClusterTesterECALShEnFWithCut1 = hltLayerClusterTesterECALShEnF.clone( +hltLayerClusterTesterECALShEnFWithCut = hltLayerClusterTesterECALShEnF.clone( enFracCut = cms.double(0.01), - ptCut = cms.double(0.) -) - -hltLayerClusterTesterECALShEnFWithCut2 = hltLayerClusterTesterECALShEnF.clone( - enFracCut = cms.double(0.), ptCut = cms.double(0.1) ) -hltLayerClusterTesterECALShEnFWithCut3 = hltLayerClusterTesterECALShEnF.clone( - enFracCut = cms.double(0.01), - ptCut = cms.double(0.1) -) hltHgcalValSeq = cms.Sequence( hltHgcalValidator) hltHgcalAndBarrelValSeq = cms.Sequence( hltHgcalValidator - +hltLayerClusterTesterECAL - +hltLayerClusterTesterECALWithCut1 - +hltLayerClusterTesterECALWithCut2 - +hltLayerClusterTesterECALWithCut3 - +hltLayerClusterTesterECALShEnF - +hltLayerClusterTesterECALShEnFWithCut1 - +hltLayerClusterTesterECALShEnFWithCut2 - +hltLayerClusterTesterECALShEnFWithCut3 + +hltLayerClusterTesterECALWithCut + +hltLayerClusterTesterECALShEnFWithCut ) from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 2feb00d592ec6..ec6663de79ea4 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -74,8 +74,8 @@ class PFTesterT : public DQMEDAnalyzer { MonitorElement* h_PFClusterHitFraction_; MonitorElement* h_PFClusterHitDetId_; - MonitorElement* h_CaloParticleToSimClusterEnergyFraction_; - MonitorElement* h_CaloParticleToSimHitsEnergyFraction_; + MonitorElement* h_CPToSCEnergyFraction_; + MonitorElement* h_CPToSHEnergyFraction_; MonitorElement* h_CP_recoToSimScore_; MonitorElement* h_CP_simToRecoScore_; MonitorElement* h_CP_simToRecoShEnF_; @@ -89,12 +89,12 @@ class PFTesterT : public DQMEDAnalyzer { MonitorElement* h_simToRecoShEnF_; MonitorElement* h_simToRecoShEnF_Score_; MonitorElement* h_simToRecoShEnF_En_; - MonitorElement* h_simToRecoShEnF_EnHits_; MonitorElement* h_simToRecoShEnF_EnFrac_; + MonitorElement* h_simToRecoShEnF_EnSimTrack_; MonitorElement* h_simToRecoShEnF_Mult_; MonitorElement* h_simToRecoScore_En_; - MonitorElement* h_simToRecoScore_EnHits_; MonitorElement* h_simToRecoScore_EnFrac_; + MonitorElement* h_simToRecoScore_EnSimTrack_; MonitorElement* h_simToRecoScore_Mult_; MonitorElement* h_SimTrackToSimHitsEnergyFraction_; @@ -115,8 +115,8 @@ class PFTesterT : public DQMEDAnalyzer { }; const std::unordered_map> histoVarsSim = { {"En", std::make_tuple(100, 0., 100.)}, - {"EnHits", std::make_tuple(100, 0., 100.)}, {"EnFrac", std::make_tuple(220, 0., 1.1)}, + {"EnSimTrack", std::make_tuple(100, 0., 100.)}, {"Pt", std::make_tuple(200, 0., 100.)}, {"PtLow", std::make_tuple(100, 0., 10.)}, {"Eta", std::make_tuple(50, -6.5, 6.5)}, @@ -127,11 +127,9 @@ class PFTesterT : public DQMEDAnalyzer { using UMap = std::unordered_map; using VUMap = std::vector; UMap h_simClusters_; - UMap h_simClustersReconstructable_; VUMap h_simClustersMatchedRecoClusters_; VUMap h_simClustersMultiMatchedRecoClusters_; UMap h_recoClusters_; - UMap h_recoClustersReconstructable_; VUMap h_recoClustersMatchedSimClusters_; VUMap h_recoClustersMultiMatchedSimClusters_; std::vector h_nSimMatchedToOneReco_; @@ -152,12 +150,12 @@ class PFTesterT : public DQMEDAnalyzer { {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"EnHits_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"EnHits_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, - {"EnHits_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, {"EnFrac_Eta", std::make_tuple(220, 0., 1.1, 50, -6.5, 6.5)}, {"EnFrac_Phi", std::make_tuple(220, 0., 1.1, 50, -3.5, 3.5)}, {"EnFrac_Mult", std::make_tuple(220, 0., 1.1, 200, 0., 200.)}, + {"EnSimTrack_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"EnSimTrack_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"EnSimTrack_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, @@ -168,10 +166,8 @@ class PFTesterT : public DQMEDAnalyzer { using U2Map = std::unordered_map; using VU2Map = std::vector>; U2Map h2d_simClusters_; - U2Map h2d_simClustersReconstructable_; VU2Map h2d_simClustersMatchedRecoClusters_; U2Map h2d_recoClusters_; - U2Map h2d_recoClustersReconstructable_; VU2Map h2d_recoClustersMatchedSimClusters_; VU2Map h2d_responsePt_; @@ -214,24 +210,16 @@ PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) template void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { - ibook.setCurrentFolder(outFolder_ + "/CaloParticles_EnFracCut" + doubleToString(enFracCut_) + "_PtCut" + - doubleToString(ptCut_)); - h_CaloParticleToSimClusterEnergyFraction_ = - ibook.book1D("CaloParticleToSimClusterEnergyFraction", - "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", - 100, - 0, - 2); - h_CaloParticleToSimHitsEnergyFraction_ = - ibook.book1D("CaloParticleToSimHitsEnergyFraction", - "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", - 100, - 0, - 2); + std::string matching = doMatchByScore_ ? "MatchByScore" : "MatchByShEnF"; + ibook.setCurrentFolder(outFolder_ + "/" + matching + "/CaloParticles"); + h_CPToSCEnergyFraction_ = + ibook.book1D("CPToSCEnergyFraction", "CPToSCEnergyFraction;CaloParticle to SimCluster energy fraction", 100, 0, 2); + h_CPToSHEnergyFraction_ = + ibook.book1D("CPToSHEnergyFraction", "CPToSHEnergyFraction;CaloParticle to SimHits energy fraction", 100, 0, 2); h_CP_recoToSimScore_ = - ibook.book1D("CP_recoToSimScore", "recoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); + ibook.book1D("CP_recoToSimScore", "CPrecoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); h_CP_simToRecoScore_ = - ibook.book1D("CP_simToRecoScore", "simToRecoScore;CaloParticle Sim #rightarrow Reco score", 51, 0, 1.02); + ibook.book1D("CP_simToRecoScore", "CPsimToRecoScore;CaloParticle Sim #rightarrow Reco score", 51, 0, 1.02); h_CP_simToRecoShEnF_ = ibook.book1D("CP_simToRecoShEnF", "simToRecoSharedEnergy;CaloParticle Sim #rightarrow Reco shared energy fraction", 51, @@ -247,9 +235,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 0, 1.02); - std::string matching = doMatchByScore_ ? "_MatchByScore" : "_MatchByShEnF"; - std::string pfValidFolder = outFolder_ + "/PFClusterValidation" + matching + "_EnFracCut" + - doubleToString(enFracCut_) + "_PtCut" + doubleToString(ptCut_); + std::string pfValidFolder = outFolder_ + "/" + matching + "/PFClusterValidation"; ibook.setCurrentFolder(pfValidFolder); h_nSimClusters_ = ibook.book1D("nSimClusters", "Number of SimClusters;Number of SimClusters per event", 100, 0, 100); h_nSimClustersPrimary_ = ibook.book1D( @@ -270,16 +256,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 1.02); h_simToRecoShEnF_En_ = ibook.book2D("simToRecoShEnF_En", - "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy", - 51, - 0, - 1.02, - 100, - 0., - 100.); - h_simToRecoShEnF_EnHits_ = - ibook.book2D("simToRecoShEnF_EnHits", - "simToRecoSharedEnergy vs Energy Hits;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", + "simToRecoSharedEnergy vs Energy;Sim #rightarrow Reco shared energy fraction;Energy_{hits}", 51, 0, 1.02, @@ -295,6 +272,15 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 220, 0., 1.1); + h_simToRecoShEnF_EnSimTrack_ = + ibook.book2D("simToRecoShEnF_EnSimTrack", + "simToRecoSharedEnergy vs SimTrack Energy;Sim #rightarrow Reco shared energy fraction;SimTrack Energy", + 51, + 0, + 1.02, + 100, + 0., + 100.); h_simToRecoShEnF_Mult_ = ibook.book2D("simToRecoShEnF_Mult", "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", @@ -304,10 +290,8 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 200, 0., 200.); - h_simToRecoScore_En_ = ibook.book2D( - "simToRecoScore_En", "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy", 51, 0, 1.02, 100, 0., 100.); - h_simToRecoScore_EnHits_ = ibook.book2D("simToRecoScore_EnHits", - "simToRecoScore vs Energy Hits;Sim #rightarrow Reco score;Energy_{hits}", + h_simToRecoScore_En_ = ibook.book2D("simToRecoScore_En", + "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy_{hits}", 51, 0, 1.02, @@ -322,6 +306,8 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 220, 0., 1.1); + h_simToRecoScore_EnSimTrack_ = ibook.book2D( + "simToRecoScore_EnSimTrack", "simToRecoScore vs SimTrack Energy;Sim #rightarrow Reco score;SimTrack Energy", 51, 0, 1.02, 100, 0., 100.); h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 51, @@ -337,36 +323,62 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 0, 1.1); - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); - h_nSimMatchedToOneReco_[ithr] = ibook.book1D( - "nSimMatchedToOneReco", - "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", - 10, - 0, - 10); - h_nRecoMatchedToOneSim_[ithr] = ibook.book1D( - "nRecoMatchedToOneSim", - "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", - 10, - 0, - 10); - } - for (auto& hVar : histoVarsSim) { auto [nBins, hMin, hMax] = hVar.second; - - ibook.setCurrentFolder(pfValidFolder); h_simClusters_[hVar.first] = ibook.book1D("SimClusters" + hVar.first, "SimClusters;" + hVar.first, nBins, hMin, hMax); - h_simClustersReconstructable_[hVar.first] = ibook.book1D( - "SimClustersReconstructable" + hVar.first, "SimClustersReconstructable;" + hVar.first, nBins, hMin, hMax); + } - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + for (auto& hVar : histoVarsReco) { + auto [nBins, hMin, hMax] = hVar.second; + h_recoClusters_[hVar.first] = + ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); + } - ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + for (auto& h2dVar : histo2dVarsSim) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); + h2d_simClusters_[h2dVar.first] = ibook.book2D("SimClusters" + h2dVar.first, + "SimClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + } + for (auto& h2dVar : histo2dVarsReco) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); + h2d_recoClusters_[h2dVar.first] = ibook.book2D("RecoClusters" + h2dVar.first, + "RecoClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + } + + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + h_nSimMatchedToOneReco_[ithr] = ibook.book1D( + "nSimMatchedToOneReco", + "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", + 10, + 0, + 10); + h_nRecoMatchedToOneSim_[ithr] = ibook.book1D( + "nRecoMatchedToOneSim", + "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", + 10, + 0, + 10); + for (auto& hVar : histoVarsSim) { + auto [nBins, hMin, hMax] = hVar.second; h_simClustersMatchedRecoClusters_[ithr][hVar.first] = ibook.book1D("SimClustersMatchedRecoClusters" + hVar.first, "SimClusters matched to RecoClusters;" + hVar.first, @@ -379,20 +391,13 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, nBins, hMin, hMax); + h2d_responsePt_[ithr][hVar.first] = + ibook.book2D("ResponsePt_" + hVar.first, "Response p_T;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); + h2d_responseE_[ithr][hVar.first] = + ibook.book2D("ResponseE_" + hVar.first, "Response Energy;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); } - } - - for (auto& hVar : histoVarsReco) { - auto [nBins, hMin, hMax] = hVar.second; - - ibook.setCurrentFolder(pfValidFolder); - h_recoClusters_[hVar.first] = - ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); - - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - - ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + for (auto& hVar : histoVarsReco) { + auto [nBins, hMin, hMax] = hVar.second; h_recoClustersMatchedSimClusters_[ithr][hVar.first] = ibook.book1D("RecoClustersMatchedSimClusters" + hVar.first, "RecoClusters matched to SimClusters;" + hVar.first, @@ -406,35 +411,10 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMin, hMax); } - } - - for (auto& h2dVar : histo2dVarsSim) { - auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; - - ibook.setCurrentFolder(pfValidFolder); - auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); - auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); - h2d_simClusters_[h2dVar.first] = ibook.book2D("SimClusters" + h2dVar.first, - "SimClusters;" + x_title + ";" + y_title, - nBinsX, - hMinX, - hMaxX, - nBinsY, - hMinY, - hMaxY); - h2d_simClustersReconstructable_[h2dVar.first] = - ibook.book2D("SimClustersReconstructable" + h2dVar.first, - "SimClustersReconstructable;" + x_title + ";" + y_title, - nBinsX, - hMinX, - hMaxX, - nBinsY, - hMinY, - hMaxY); - - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + for (auto& h2dVar : histo2dVarsSim) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_simClustersMatchedRecoClusters_[ithr][h2dVar.first] = ibook.book2D("SimClustersMatchedRecoClusters" + h2dVar.first, "SimClusters matched to RecoClusters;" + x_title + ";" + y_title, @@ -445,27 +425,10 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMinY, hMaxY); } - } - - for (auto& h2dVar : histo2dVarsReco) { - auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; - - ibook.setCurrentFolder(pfValidFolder); - auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); - auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); - h2d_recoClusters_[h2dVar.first] = ibook.book2D("RecoClusters" + h2dVar.first, - "RecoClusters;" + x_title + ";" + y_title, - nBinsX, - hMinX, - hMaxX, - nBinsY, - hMinY, - hMaxY); - - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - - ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + for (auto& h2dVar : histo2dVarsReco) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_recoClustersMatchedSimClusters_[ithr][h2dVar.first] = ibook.book2D("RecoClustersMatchedSimClusters" + h2dVar.first, "RecoClusters matched to SimClusters;" + x_title + ";" + y_title, @@ -478,19 +441,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, } } - for (auto& hVar : histoVarsSim) { - auto [nBins, hMin, hMax] = hVar.second; - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { - std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); - ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); - h2d_responsePt_[ithr][hVar.first] = - ibook.book2D("ResponsePt_" + hVar.first, "Response p_T;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); - h2d_responseE_[ithr][hVar.first] = - ibook.book2D("ResponseE_" + hVar.first, "Response Energy;" + hVar.first, nBins, hMin, hMax, 50, 0., 2.); - } - } - - ibook.setCurrentFolder(outFolder_ + "/PFCandidates"); + ibook.setCurrentFolder(outFolder_ + "/" + matching + "/PFCandidates"); h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); @@ -498,7 +449,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, h_PFCandPdgId_ = ibook.book1D("PFCandPdgId", "PFCandPdgId", 44, -22, 22); h_PFCandType_ = ibook.book1D("PFCandidateType", "PFCandidateType", 10, 0, 10); - ibook.setCurrentFolder(outFolder_ + "/PFBlocks"); + ibook.setCurrentFolder(outFolder_ + "/" + matching + "/PFBlocks"); h_NumElements_ = ibook.book1D("NumElements", "NumElements", 25, 0, 25); h_NumTrackElements_ = ibook.book1D("NumTrackElements", "NumTrackElements", 5, 0, 5); h_NumMuonElements_ = ibook.book1D("NumMuonElements", "NumMuonElements", 5, 0, 5); @@ -508,13 +459,13 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, h_NumHCALElements_ = ibook.book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); h_NumHGCALElements_ = ibook.book1D("NumHGCALElements", "NumHGCALElements", 5, 0, 5); - ibook.setCurrentFolder(outFolder_ + "/PFTracks"); + ibook.setCurrentFolder(outFolder_ + "/" + matching + "/PFTracks"); h_TrackCharge_ = ibook.book1D("TrackCharge", "TrackCharge", 5, -2, 2); h_TrackNumPoints_ = ibook.book1D("TrackNumPoints", "TrackNumPoints", 100, 0, 100); h_TrackNumMeasurements_ = ibook.book1D("TrackNumMeasurements", "TrackNumMeasurements", 100, 0, 100); h_TrackImpactParameter_ = ibook.book1D("TrackImpactParameter", "TrackImpactParameter", 1000, 0, 1); - ibook.setCurrentFolder(outFolder_ + "/PFClusters"); + ibook.setCurrentFolder(outFolder_ + "/" + matching + "/PFClusters"); h_PFClusterE_ = ibook.book1D("PFClusterE", "RecoCluster Energy;E [GeV]", 100, 0, 100); h_PFClusterEta_ = ibook.book1D("PFClusterEta", "RecoCluster Eta;#eta", 120, -6, 6); h_PFClusterPhi_ = ibook.book1D("PFClusterPhi", "RecoCluster Phi;#phi", 128, -3.2, 3.2); @@ -642,8 +593,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e << ", energyFracSumSimHits=" << energyFracSumSimHits << std::endl; #endif - h_CaloParticleToSimClusterEnergyFraction_->Fill(energySumSimClusters / caloParticles[cpId].energy()); - h_CaloParticleToSimHitsEnergyFraction_->Fill(energySumSimHits / caloParticles[cpId].energy()); + h_CPToSCEnergyFraction_->Fill(energySumSimClusters / caloParticles[cpId].energy()); + h_CPToSHEnergyFraction_->Fill(energySumSimHits / caloParticles[cpId].energy()); // SimToReco association for caloParticles const edm::Ref caloParticleRef(CaloParticle, cpId); @@ -727,24 +678,24 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e ++nSimClustersPrimary; // efficiency and split denominator - h_simClusters_["En"]->Fill(simClusters[simId].energy()); - h_simClusters_["EnHits"]->Fill(energySumSimHits); + h_simClusters_["En"]->Fill(energySumSimHits); h_simClusters_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); + h_simClusters_["EnSimTrack"]->Fill(simClusters[simId].energy()); h_simClusters_["Pt"]->Fill(simClusters[simId].pt()); h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); h_simClusters_["Eta"]->Fill(simTrackEtaAtBoundary); h_simClusters_["Phi"]->Fill(simClusters[simId].phi()); h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); - h2d_simClusters_["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); - h2d_simClusters_["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); - h2d_simClusters_["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); - h2d_simClusters_["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); - h2d_simClusters_["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClusters_["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); + h2d_simClusters_["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); + h2d_simClusters_["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClusters_["En_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); h2d_simClusters_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); h2d_simClusters_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); h2d_simClusters_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClusters_["EnSimTrack_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); + h2d_simClusters_["EnSimTrack_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); + h2d_simClusters_["EnSimTrack_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); h2d_simClusters_["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClusters_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClusters_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); @@ -758,31 +709,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e const auto& simToRecoMatched = simToRecoIt->val; if (simToRecoMatched.empty()) continue; - - h_simClustersReconstructable_["En"]->Fill(simClusters[simId].energy()); - h_simClustersReconstructable_["EnHits"]->Fill(energySumSimHits); - h_simClustersReconstructable_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); - h_simClustersReconstructable_["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersReconstructable_["PtLow"]->Fill(simClusters[simId].pt()); - h_simClustersReconstructable_["Eta"]->Fill(simTrackEtaAtBoundary); - h_simClustersReconstructable_["Phi"]->Fill(simClusters[simId].phi()); - h_simClustersReconstructable_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); - - h2d_simClustersReconstructable_["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); - h2d_simClustersReconstructable_["En_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); - h2d_simClustersReconstructable_["En_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); - h2d_simClustersReconstructable_["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClustersReconstructable_["EnHits_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); - h2d_simClustersReconstructable_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); - h2d_simClustersReconstructable_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, - simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); - h2d_simClustersReconstructable_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); - h2d_simClustersReconstructable_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); - h2d_simClustersReconstructable_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); - h2d_simClustersReconstructable_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { const double& thresh = assocScoreThresholds_[ithr]; @@ -828,13 +754,13 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h_simToRecoScore_->Fill(score); h_simToRecoShEnF_->Fill(shared_energy_frac); h_simToRecoShEnF_Score_->Fill(shared_energy_frac, score); - h_simToRecoShEnF_En_->Fill(shared_energy_frac, simClusters[simId].energy()); - h_simToRecoShEnF_EnHits_->Fill(shared_energy_frac, energySumSimHits); + h_simToRecoShEnF_En_->Fill(shared_energy_frac, energySumSimHits); h_simToRecoShEnF_EnFrac_->Fill(shared_energy_frac, SimClusterToCPEnergyFraction); + h_simToRecoShEnF_EnSimTrack_->Fill(shared_energy_frac, simClusters[simId].energy()); h_simToRecoShEnF_Mult_->Fill(shared_energy_frac, simClusters[simId].numberOfRecHits()); - h_simToRecoScore_En_->Fill(score, simClusters[simId].energy()); - h_simToRecoScore_EnHits_->Fill(score, energySumSimHits); + h_simToRecoScore_En_->Fill(score, energySumSimHits); h_simToRecoScore_EnFrac_->Fill(score, SimClusterToCPEnergyFraction); + h_simToRecoScore_EnSimTrack_->Fill(score, simClusters[simId].energy()); h_simToRecoScore_Mult_->Fill(score, simClusters[simId].numberOfRecHits()); if (doMatchByScore_) { @@ -852,23 +778,18 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // efficiency numerator if (nRecoMatchedToOneSim > 0) { - h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); - h_simClustersMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); + h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(energySumSimHits); h_simClustersMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); + h_simClustersMatchedRecoClusters_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy()); h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(simClusters[simId].energy(), - simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(simClusters[simId].energy(), - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnHits_Mult"]->Fill(energySumSimHits, + h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); @@ -876,6 +797,11 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Phi"]->Fill(simClusters[simId].energy(), + simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Mult"]->Fill(simClusters[simId].energy(), + simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), @@ -887,9 +813,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // split numerator if (nRecoMatchedToOneSim > 1) { - h_simClustersMultiMatchedRecoClusters_[ithr]["En"]->Fill(simClusters[simId].energy()); - h_simClustersMultiMatchedRecoClusters_[ithr]["EnHits"]->Fill(energySumSimHits); + h_simClustersMultiMatchedRecoClusters_[ithr]["En"]->Fill(energySumSimHits); h_simClustersMultiMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); + h_simClustersMultiMatchedRecoClusters_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy()); h_simClustersMultiMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); h_simClustersMultiMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); @@ -1077,11 +1003,12 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e } if (passMatch) { - // h2d_responsePt_[ithr]["En"]->Fill(simClusters[simId].energy(), + // h2d_responsePt_[ithr]["En"]->Fill(energySumSimHits, // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].pt() / simClusters[simId].pt()); // h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, // recoClusters[recoId].pt() / simClusters[simId].pt()); + // h2d_responsePt_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), + // recoClusters[recoId].pt() / simClusters[simId].pt()); // h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), // recoClusters[recoId].pt() / simClusters[simId].pt()); // h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, @@ -1091,11 +1018,12 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), // recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responseE_[ithr]["En"]->Fill(simClusters[simId].energy(), + h2d_responseE_[ithr]["En"]->Fill(energySumSimHits, recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["EnHits"]->Fill(energySumSimHits, recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), + recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 4227e3a9c86f9..1c1f02124c51e 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -10,22 +10,21 @@ efficiency = cms.vstring( *[ item for thr in _thresholds - for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) for item in ( - f"'Score{thr}/Eff_vs_EnEta{name}' 'Efficiency vs Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClusters{suf}En_Eta", - f"'Score{thr}/Eff_vs_EnPhi{name}' 'Efficiency vs Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClusters{suf}En_Phi", - f"'Score{thr}/Eff_vs_EnMult{name}' 'Efficiency vs Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClusters{suf}En_Mult", - f"'Score{thr}/Eff_vs_EnHitsEta{name}' 'Efficiency vs Hits Energy-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Eta SimClusters{suf}EnHits_Eta", - f"'Score{thr}/Eff_vs_EnHitsPhi{name}' 'Efficiency vs Hits Energy-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Phi SimClusters{suf}EnHits_Phi", - f"'Score{thr}/Eff_vs_EnHitsMult{name}' 'Efficiency vs Hits Energy-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits_Mult SimClusters{suf}EnHits_Mult", - f"'Score{thr}/Eff_vs_EnFracEta{name}' 'Efficiency vs Energy fraction-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Eta SimClusters{suf}EnFrac_Eta", - f"'Score{thr}/Eff_vs_EnFracPhi{name}' 'Efficiency vs Energy fraction-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Phi SimClusters{suf}EnFrac_Phi", - f"'Score{thr}/Eff_vs_EnFracMult{name}' 'Efficiency vs Energy fraction-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Mult SimClusters{suf}EnFrac_Mult", - f"'Score{thr}/Eff_vs_PtEta{name}' 'Efficiency vs p_{{T}}-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClusters{suf}Pt_Eta", - f"'Score{thr}/Eff_vs_PtPhi{name}' 'Efficiency vs p_{{T}}-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClusters{suf}Pt_Phi", - f"'Score{thr}/Eff_vs_PtMult{name}' 'Efficiency vs p_{{T}}-Mult {suf}' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClusters{suf}Pt_Mult", - f"'Score{thr}/Eff_vs_MultEta{name}' 'Efficiency vs Mult-#eta {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClusters{suf}Mult_Eta", - f"'Score{thr}/Eff_vs_MultPhi{name}' 'Efficiency vs Mult-#phi {suf}' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClusters{suf}Mult_Phi", + f"'Score{thr}/Eff_vs_EnEta' 'Efficiency vs Energy-#eta' Score{thr}/SimClustersMatchedRecoClustersEn_Eta SimClustersEn_Eta", + f"'Score{thr}/Eff_vs_EnPhi' 'Efficiency vs Energy-#phi' Score{thr}/SimClustersMatchedRecoClustersEn_Phi SimClustersEn_Phi", + f"'Score{thr}/Eff_vs_EnMult' 'Efficiency vs Energy-Mult' Score{thr}/SimClustersMatchedRecoClustersEn_Mult SimClustersEn_Mult", + f"'Score{thr}/Eff_vs_EnFracEta' 'Efficiency vs Energy fraction-#eta' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Eta SimClustersEnFrac_Eta", + f"'Score{thr}/Eff_vs_EnFracPhi' 'Efficiency vs Energy fraction-#phi' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Phi SimClustersEnFrac_Phi", + f"'Score{thr}/Eff_vs_EnFracMult' 'Efficiency vs Energy fraction-Mult' Score{thr}/SimClustersMatchedRecoClustersEnFrac_Mult SimClustersEnFrac_Mult", + f"'Score{thr}/Eff_vs_EnSimTrackEta' 'Efficiency vs SimTrack Energy-#eta' Score{thr}/SimClustersMatchedRecoClustersEnSimTrack_Eta SimClustersEnSimTrack_Eta", + f"'Score{thr}/Eff_vs_EnSimTrackPhi' 'Efficiency vs SimTrack Energy-#phi' Score{thr}/SimClustersMatchedRecoClustersEnSimTrack_Phi SimClustersEnSimTrack_Phi", + f"'Score{thr}/Eff_vs_EnSimTrackMult' 'Efficiency vs SimTrack Energy-Mult' Score{thr}/SimClustersMatchedRecoClustersEnSimTrack_Mult SimClustersEnSimTrack_Mult", + f"'Score{thr}/Eff_vs_PtEta' 'Efficiency vs p_{{T}}-#eta' Score{thr}/SimClustersMatchedRecoClustersPt_Eta SimClustersPt_Eta", + f"'Score{thr}/Eff_vs_PtPhi' 'Efficiency vs p_{{T}}-#phi' Score{thr}/SimClustersMatchedRecoClustersPt_Phi SimClustersPt_Phi", + f"'Score{thr}/Eff_vs_PtMult' 'Efficiency vs p_{{T}}-Mult' Score{thr}/SimClustersMatchedRecoClustersPt_Mult SimClustersPt_Mult", + f"'Score{thr}/Eff_vs_MultEta' 'Efficiency vs Mult-#eta' Score{thr}/SimClustersMatchedRecoClustersMult_Eta SimClustersMult_Eta", + f"'Score{thr}/Eff_vs_MultPhi' 'Efficiency vs Mult-#phi' Score{thr}/SimClustersMatchedRecoClustersMult_Phi SimClustersMult_Phi", ) ], *[ item @@ -45,24 +44,23 @@ efficiencyProfile = cms.untracked.vstring( # for smoother rebinning *[ item for thr in _thresholds - for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')) for item in ( # Efficiency - f"'Score{thr}/Eff_vs_En{name}' 'Efficiency vs Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEn SimClusters{suf}En", - f"'Score{thr}/Eff_vs_EnHits{name}' 'Efficiency vs Hits Energy {suf}' Score{thr}/SimClustersMatchedRecoClustersEnHits SimClusters{suf}EnHits", - f"'Score{thr}/Eff_vs_EnFrac{name}' 'Efficiency vs Energy fraction {suf}' Score{thr}/SimClustersMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", - f"'Score{thr}/Eff_vs_Pt{name}' 'Efficiency vs p_{{T}} {suf}' Score{thr}/SimClustersMatchedRecoClustersPt SimClusters{suf}Pt", - f"'Score{thr}/Eff_vs_Eta{name}' 'Efficiency vs #eta {suf}' Score{thr}/SimClustersMatchedRecoClustersEta SimClusters{suf}Eta", - f"'Score{thr}/Eff_vs_Phi{name}' 'Efficiency vs #phi {suf}' Score{thr}/SimClustersMatchedRecoClustersPhi SimClusters{suf}Phi", - f"'Score{thr}/Eff_vs_Mult{name}' 'Efficiency vs Multiplicity {suf}' Score{thr}/SimClustersMatchedRecoClustersMult SimClusters{suf}Mult", + f"'Score{thr}/Eff_vs_En' 'Efficiency vs Energy' Score{thr}/SimClustersMatchedRecoClustersEn SimClustersEn", + f"'Score{thr}/Eff_vs_EnFrac' 'Efficiency vs Energy fraction' Score{thr}/SimClustersMatchedRecoClustersEnFrac SimClustersEnFrac", + f"'Score{thr}/Eff_vs_EnSimTrack' 'Efficiency vs SimTrack Energy' Score{thr}/SimClustersMatchedRecoClustersEnSimTrack SimClustersEnSimTrack", + f"'Score{thr}/Eff_vs_Pt' 'Efficiency vs p_{{T}}' Score{thr}/SimClustersMatchedRecoClustersPt SimClustersPt", + f"'Score{thr}/Eff_vs_Eta' 'Efficiency vs #eta' Score{thr}/SimClustersMatchedRecoClustersEta SimClustersEta", + f"'Score{thr}/Eff_vs_Phi' 'Efficiency vs #phi' Score{thr}/SimClustersMatchedRecoClustersPhi SimClustersPhi", + f"'Score{thr}/Eff_vs_Mult' 'Efficiency vs Multiplicity' Score{thr}/SimClustersMatchedRecoClustersMult SimClustersMult", # Split rate - f"'Score{thr}/Split_vs_En{name}' 'Split Rate vs Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClusters{suf}En", - f"'Score{thr}/Split_vs_EnHits{name}' 'Split Rate vs Hits Energy {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnHits SimClusters{suf}EnHits", - f"'Score{thr}/Split_vs_EnFrac{name}' 'Split Rate vs Energy fraction {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClusters{suf}EnFrac", - f"'Score{thr}/Split_vs_Pt{name}' 'Split Rate vs p_{{T}} {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClusters{suf}Pt", - f"'Score{thr}/Split_vs_Eta{name}' 'Split Rate vs #eta {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClusters{suf}Eta", - f"'Score{thr}/Split_vs_Phi{name}' 'Split Rate vs #phi {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClusters{suf}Phi", - f"'Score{thr}/Split_vs_Mult{name}' 'Split Rate vs Multiplicity {suf}' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClusters{suf}Mult", + f"'Score{thr}/Split_vs_En' 'Split Rate vs Energy' Score{thr}/SimClustersMultiMatchedRecoClustersEn SimClustersEn", + f"'Score{thr}/Split_vs_EnFrac' 'Split Rate vs Energy fraction' Score{thr}/SimClustersMultiMatchedRecoClustersEnFrac SimClustersEnFrac", + f"'Score{thr}/Split_vs_EnSimTrack' 'Split Rate vs SimTrack Energy' Score{thr}/SimClustersMultiMatchedRecoClustersEnSimTrack SimClustersEnSimTrack", + f"'Score{thr}/Split_vs_Pt' 'Split Rate vs p_{{T}}' Score{thr}/SimClustersMultiMatchedRecoClustersPt SimClustersPt", + f"'Score{thr}/Split_vs_Eta' 'Split Rate vs #eta' Score{thr}/SimClustersMultiMatchedRecoClustersEta SimClustersEta", + f"'Score{thr}/Split_vs_Phi' 'Split Rate vs #phi' Score{thr}/SimClustersMultiMatchedRecoClustersPhi SimClustersPhi", + f"'Score{thr}/Split_vs_Mult' 'Split Rate vs Multiplicity' Score{thr}/SimClustersMultiMatchedRecoClustersMult SimClustersMult", ) ], *[ item @@ -89,8 +87,8 @@ for thr in _thresholds for item in ( f"'Score{thr}/ResponseE_En' 'Response vs Energy' Score{thr}/ResponseE_En rms", - f"'Score{thr}/ResponseE_EnHits' 'Response vs Hits Energy' Score{thr}/ResponseE_EnHits rms", f"'Score{thr}/ResponseE_EnFrac' 'Response vs Energy fraction' Score{thr}/ResponseE_EnFrac rms", + f"'Score{thr}/ResponseE_EnSimTrack' 'Response vs SimTrack Energy' Score{thr}/ResponseE_EnSimTrack rms", f"'Score{thr}/ResponseE_Pt' 'Response vs p_{{T}}' Score{thr}/ResponseE_Pt rms", f"'Score{thr}/ResponseE_Eta' 'Response vs #eta' Score{thr}/ResponseE_Eta rms", f"'Score{thr}/ResponseE_Phi' 'Response vs #phi' Score{thr}/ResponseE_Phi rms", diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 498caa93fc58c..df5e85a413582 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -42,18 +42,8 @@ from Configuration.Eras.Modifier_phase2_common_cff import phase2_common phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp")) -hltPFClusterTesterECALWithCut1 = hltPFClusterTesterECAL.clone( +hltPFClusterTesterECALWithCut = hltPFClusterTesterECAL.clone( enFracCut = cms.double(0.01), - ptCut = cms.double(0.) -) - -hltPFClusterTesterECALWithCut2 = hltPFClusterTesterECAL.clone( - enFracCut = cms.double(0.), - ptCut = cms.double(0.1) -) - -hltPFClusterTesterECALWithCut3 = hltPFClusterTesterECAL.clone( - enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) @@ -62,18 +52,8 @@ doMatchByScore = cms.bool(False) ) -hltPFClusterTesterECALShEnFWithCut1 = hltPFClusterTesterECALShEnF.clone( +hltPFClusterTesterECALShEnFWithCut = hltPFClusterTesterECALShEnF.clone( enFracCut = cms.double(0.01), - ptCut = cms.double(0.) -) - -hltPFClusterTesterECALShEnFWithCut2 = hltPFClusterTesterECALShEnF.clone( - enFracCut = cms.double(0.), - ptCut = cms.double(0.1) -) - -hltPFClusterTesterECALShEnFWithCut3 = hltPFClusterTesterECALShEnF.clone( - enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) @@ -82,12 +62,6 @@ +hltPFClusterSimClusterAssociationProducerECAL +hltPFCpAssocByEnergyScoreProducer +hltPFClusterCaloParticleAssociationProducerECAL - +hltPFClusterTesterECAL - +hltPFClusterTesterECALWithCut1 - +hltPFClusterTesterECALWithCut2 - +hltPFClusterTesterECALWithCut3 - +hltPFClusterTesterECALShEnF - +hltPFClusterTesterECALShEnFWithCut1 - +hltPFClusterTesterECALShEnFWithCut2 - +hltPFClusterTesterECALShEnFWithCut3 + +hltPFClusterTesterECALWithCut + +hltPFClusterTesterECALShEnFWithCut ) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 2e57e76513fea..9524eb66fb8df 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -236,7 +236,7 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): baseline=None, color=next(colors_iter)) sublabel = re.sub(pattern, replacement, sub) - if sublabel == f'{matching} = 1.1': sublabel = f'{matching} = 1.0' + if sublabel == f'{matching} = 1.1': sublabel = f'{matching} = 1.0' # Temporary fix if any(x in name for x in ('Eff', 'Fake', 'Split', 'Merge')): eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], @@ -298,7 +298,8 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): line = plotter.ax.stairs(ratio_vals, bin_edges, linewidth=2, baseline=None, color=next(colors_iter)) - sublabel = re.sub(pattern, replacement, sub) + sublabel = re.sub(pattern, replacement, sub) + if sublabel == f'{matching} = 1.1': sublabel = f'{matching} = 1.0' # Temporary fix plotter.ax.errorbar(bin_centers, ratio_vals, xerr=None, yerr=ratio_errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) @@ -478,8 +479,6 @@ def __call__(self, parser, namespace, values, option_string=None): parser.add_argument('-o', '--odir', default="HLTPFValidationPlots", help='Path to the output directory.') parser.add_argument('-l', '--sample_label', default="", help='Sample label for plotting.') parser.add_argument('-e', '--era', default="Phase2", help="Chose between ['Phase2', 'Run3'].") - parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') - parser.add_argument('--PtCut', default=0.01, help='Cut on the sim cluster energy fraction.') parser.add_argument('--match_by_score', default=1, type=int, help='Use association based on score (if false, use shared energy fraction).') parser.add_argument('--ticl', default=False, action='store_true', help='Use TiclBarrel folder.') @@ -518,13 +517,13 @@ def __call__(self, parser, namespace, values, option_string=None): 'merge': 'Merge Rate'} if args.match_by_score == 1: - matching = '_MatchByScore' + matching = 'MatchByScore' else: - matching = '_MatchByShEnF' + matching = 'MatchByShEnF' print("### INFO: Using association by shared energy fraction.") if args.ticl: sub_folder = 'TiclBarrel' else: sub_folder = 'ParticleFlow' - dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/PFClusterValidation{matching}_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" + dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/{matching}/PFClusterValidation" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) @@ -550,71 +549,70 @@ def __call__(self, parser, namespace, values, option_string=None): createDir(f'{args.odir}/{subdir}') createIndexPHP(src=args.odir, dest=f'{args.odir}/{subdir}') - for name, suf in zip(('', '_Reconstructable'), ('', 'Reconstructable')): - varsDict = { - # Cluster efficiency - f'{subdir}/Eff_vs_En{name}': dict(ratio=f'{subdir}/Eff_vs_En{name}', - den=f'SimClusters{suf}En', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersEn', legnum='Matched SimClusters', - xtitle='Energy from SimTrack [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_EnHits{name}': dict(ratio=f'{subdir}/Eff_vs_EnHits{name}', - den=f'SimClusters{suf}EnHits', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersEnHits', legnum='Matched SimClusters', - xtitle='Energy from hits [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_EnFrac{name}': dict(ratio=f'{subdir}/Eff_vs_EnFrac{name}', - den=f'SimClusters{suf}EnFrac', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersEnFrac', legnum='Matched SimClusters', - xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_Pt{name}': dict(ratio=f'{subdir}/Eff_vs_Pt{name}', - den=f'SimClusters{suf}Pt', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersPt', legnum='Matched SimClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_Eta{name}': dict(ratio=f'{subdir}/Eff_vs_Eta{name}', - den=f'SimClusters{suf}Eta', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersEta', legnum='Matched SimClusters', - xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Eff_vs_Phi{name}': dict(ratio=f'{subdir}/Eff_vs_Phi{name}', - den=f'SimClusters{suf}Phi', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersPhi', legnum='Matched SimClusters', - xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Eff_vs_Mult{name}': dict(ratio=f'{subdir}/Eff_vs_Mult{name}', - den=f'SimClusters{suf}Mult', legden='SimClusters', - num=f'{subdir}/SimClustersMatchedRecoClustersMult', legnum='Matched SimClusters', - xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - # Cluster split rate - f'{subdir}/Split_vs_En{name}': dict(ratio=f'{subdir}/Split_vs_En{name}', - den=f'SimClusters{suf}En', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersEn', legnum='Multi Matched SimClusters', - xtitle='Energy from SimTrack [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_EnHits{name}': dict(ratio=f'{subdir}/Split_vs_EnHits{name}', - den=f'SimClusters{suf}EnHits', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnHits', legnum='Multi Matched SimClusters', - xtitle='Energy from hits [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_EnFrac{name}': dict(ratio=f'{subdir}/Split_vs_EnFrac{name}', - den=f'SimClusters{suf}EnFrac', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnFrac', legnum='Multi Matched SimClusters', - xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_Pt{name}': dict(ratio=f'{subdir}/Split_vs_Pt{name}', - den=f'SimClusters{suf}Pt', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersPt', legnum='Multi Matched SimClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_Eta{name}': dict(ratio=f'{subdir}/Split_vs_Eta{name}', - den=f'SimClusters{suf}Eta', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersEta', legnum='Multi Matched SimClusters', - xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Split_vs_Phi{name}': dict(ratio=f'{subdir}/Split_vs_Phi{name}', - den=f'SimClusters{suf}Phi', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersPhi', legnum='Multi Matched SimClusters', - xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Split_vs_Mult{name}': dict(ratio=f'{subdir}/Split_vs_Mult{name}', - den=f'SimClusters{suf}Mult', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersMult', legnum='Multi Matched SimClusters', - xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - } - - # Compare pairs of variables - for title, props in varsDict.items(): - plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') + varsDict = { + # Cluster efficiency + f'{subdir}/Eff_vs_En': dict(ratio=f'{subdir}/Eff_vs_En', + den=f'SimClustersEn', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEn', legnum='Matched SimClusters', + xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_EnFrac': dict(ratio=f'{subdir}/Eff_vs_EnFrac', + den=f'SimClustersEnFrac', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEnFrac', legnum='Matched SimClusters', + xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_EnSimTrack': dict(ratio=f'{subdir}/Eff_vs_EnSimTrack', + den=f'SimClustersEnSimTrack', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEnSimTrack', legnum='Matched SimClusters', + xtitle='SimTrack Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_Pt': dict(ratio=f'{subdir}/Eff_vs_Pt', + den=f'SimClustersPt', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersPt', legnum='Matched SimClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Eff_vs_Eta': dict(ratio=f'{subdir}/Eff_vs_Eta', + den=f'SimClustersEta', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersEta', legnum='Matched SimClusters', + xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Eff_vs_Phi': dict(ratio=f'{subdir}/Eff_vs_Phi', + den=f'SimClustersPhi', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersPhi', legnum='Matched SimClusters', + xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Eff_vs_Mult': dict(ratio=f'{subdir}/Eff_vs_Mult', + den=f'SimClustersMult', legden='SimClusters', + num=f'{subdir}/SimClustersMatchedRecoClustersMult', legnum='Matched SimClusters', + xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + # Cluster split rate + f'{subdir}/Split_vs_En': dict(ratio=f'{subdir}/Split_vs_En', + den=f'SimClustersEn', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEn', legnum='Multi Matched SimClusters', + xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Split_vs_EnFrac': dict(ratio=f'{subdir}/Split_vs_EnFrac', + den=f'SimClustersEnFrac', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnFrac', legnum='Multi Matched SimClusters', + xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Split_vs_EnSimTrack': dict(ratio=f'{subdir}/Split_vs_EnSimTrack', + den=f'SimClustersEnSimTrack', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnSimTrack', legnum='Multi Matched SimClusters', + xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Split_vs_Pt': dict(ratio=f'{subdir}/Split_vs_Pt', + den=f'SimClustersPt', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersPt', legnum='Multi Matched SimClusters', + xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + f'{subdir}/Split_vs_Eta': dict(ratio=f'{subdir}/Split_vs_Eta', + den=f'SimClustersEta', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEta', legnum='Multi Matched SimClusters', + xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Split_vs_Phi': dict(ratio=f'{subdir}/Split_vs_Phi', + den=f'SimClustersPhi', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersPhi', legnum='Multi Matched SimClusters', + xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), + f'{subdir}/Split_vs_Mult': dict(ratio=f'{subdir}/Split_vs_Mult', + den=f'SimClustersMult', legden='SimClusters', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersMult', legnum='Multi Matched SimClusters', + xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + } + + # Compare pairs of variables + for title, props in varsDict.items(): + plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') varsDict = { # Cluster fake rate @@ -666,30 +664,23 @@ def __call__(self, parser, namespace, values, option_string=None): plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') varsOverlay = { - "ResponseE_En_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='Energy from SimTrack [GeV]', logy=False), - "ResponseE_EnHits_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='Energy from hits [GeV]', logy=False), + "ResponseE_En_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='SimCluster Energy [GeV]', logy=False), "ResponseE_EnFrac_Mean" : dict(ytitle=titles['response'], rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), xtitle='Energy Fraction', logy=False), + "ResponseE_EnSimTrack_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='SimTrack Energy [GeV]', logy=False), "ResponseE_Pt_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle=r'$p_{T} [GeV]$', logy=False), "ResponseE_Eta_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\eta$', logy=False), "ResponseE_Phi_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\phi$', logy=False), "ResponseE_Mult_Mean" : dict(ytitle=titles['response'], rebin=4, xtitle='Multiplicity', logy=False), - "Eff_vs_En" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), - "Eff_vs_En_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), - "Eff_vs_EnHits" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), - "Eff_vs_EnHits_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), + "Eff_vs_En" : dict(ytitle=titles['eff'], rebin=4, xtitle='SimCluster Energy [GeV]', logy=False), "Eff_vs_EnFrac" : dict(ytitle=titles['eff'], rebin=6, xtitle='Energy Fraction', logy=False), - "Eff_vs_EnFrac_Reconstructable" : dict(ytitle=titles['eff'], rebin=6, xtitle='Energy Fraction', logy=False), + "Eff_vs_EnSimTrack" : dict(ytitle=titles['eff'], rebin=4, xtitle='SimTrack Energy [GeV]', logy=False), "Eff_vs_Pt" : dict(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Eff_vs_Pt_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), "Eff_vs_Eta" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), - "Eff_vs_Eta_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), "Eff_vs_Phi" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), - "Eff_vs_Phi_Reconstructable" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), "Eff_vs_Mult" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), - "Eff_vs_Mult_Reconstructable" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), - "Split_vs_En" : dict(ytitle=titles['split'], rebin=4, xtitle='Energy from SimTrack [GeV]', logy=False), - "Split_vs_EnHits" : dict(ytitle=titles['split'], rebin=4, xtitle='Energy from hits [GeV]', logy=False), + "Split_vs_En" : dict(ytitle=titles['split'], rebin=4, xtitle='SimCluster Energy [GeV]', logy=False), "Split_vs_EnFrac" : dict(ytitle=titles['split'], rebin=6, xtitle='Energy Fraction', logy=False), + "Split_vs_EnSimTrack" : dict(ytitle=titles['split'], rebin=4, xtitle='SimTrack Energy [GeV]', logy=False), "Split_vs_Pt" : dict(ytitle=titles['split'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), "Split_vs_Eta" : dict(ytitle=titles['split'], rebin=None, xtitle=r'$\eta$', logy=False), "Split_vs_Phi" : dict(ytitle=titles['split'], rebin=None, xtitle=r'$\phi$', logy=False), @@ -709,9 +700,9 @@ def __call__(self, parser, namespace, values, option_string=None): plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) varsResponse = { - ("ResponseE_En_Sigma", "ResponseE_En_Mean") : dict(name='ResolutionEn', ytitle=titles['resolution'], xtitle=r'$E [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), - ("ResponseE_EnHits_Sigma", "ResponseE_EnHits_Mean") : dict(name='ResolutionEnHits', ytitle=titles['resolution'], xtitle=r'$E_{hits} [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), - ("ResponseE_EnFrac_Sigma", "ResponseE_EnFrac_Mean") : dict(name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle=r'Energy Fraction', rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), logy=False), + ("ResponseE_En_Sigma", "ResponseE_En_Mean") : dict(name='ResolutionEn', ytitle=titles['resolution'], xtitle='SimCluster Energy [GeV]', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), + ("ResponseE_EnFrac_Sigma", "ResponseE_EnFrac_Mean") : dict(name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle='Energy Fraction', rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), logy=False), + ("ResponseE_EnSimTrack_Sigma", "ResponseE_EnSimTrack_Mean") : dict(name='ResolutionEnSimTrack', ytitle=titles['resolution'], xtitle='SimTrack Energy [GeV]', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), ("ResponseE_Pt_Sigma", "ResponseE_Pt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), ("ResponseE_Eta_Sigma", "ResponseE_Eta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', rebin=None, logy=False), ("ResponseE_Phi_Sigma", "ResponseE_Phi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', rebin=None, logy=False), @@ -722,13 +713,13 @@ def __call__(self, parser, namespace, values, option_string=None): vars2DProjection = { **{f'{subdir}/ResponseE_En': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_EnHits': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$E_{hits}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, **{f'{subdir}/ResponseE_EnFrac': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'Energy Fraction', unit='', logy=False, rebin=(0., 0.1, 0.5, 0.9, 1.)) for subdir in subdirs}, + **{f'{subdir}/ResponseE_EnSimTrack': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$E_{SimTrack}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, **{f'{subdir}/ResponseE_Pt': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, **{f'{subdir}/ResponseE_Eta': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)) for subdir in subdirs}, **{f'{subdir}/ResponseE_Phi': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -1.5, 0., 1.5, 3.15)) for subdir in subdirs}, **{f'{subdir}/ResponseE_Mult': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 50., 100., 200.)) for subdir in subdirs}, - 'SimClustersReconstructableEnFrac_Mult': dict(xtitle='Multiplicity', ytitle='# Clusters', var='Energy Fraction', unit='', logy=True, rebin=(0., 0.005, 0.01, 0.02, 0.03, 1.)), + 'SimClustersEnFrac_Mult': dict(xtitle='Multiplicity', ytitle='# Clusters', var='Energy Fraction', unit='', logy=True, rebin=(0., 0.005, 0.01, 0.02, 0.03, 1.)), } for name, props in vars2DProjection.items(): @@ -737,14 +728,14 @@ def __call__(self, parser, namespace, values, option_string=None): plotProject(root_hist, props, rebin_edges=props['rebin'], outname=os.path.join(args.odir, name + '_Projected')) vars2D = { - 'SimClustersReconstructableEnFrac_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='Energy Fraction'), - 'simToRecoScore_En': dict(ytitle='Energy from SimTrack [GeV]', var='# Clusters', xtitle='SimToReco Score'), - 'simToRecoScore_EnHits': dict(ytitle='Energy from hits [GeV]', var='# Clusters', xtitle='SimToReco Score'), + 'SimClustersEnFrac_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='Energy Fraction'), + 'simToRecoScore_En': dict(ytitle='SimCluster Energy [GeV]', var='# Clusters', xtitle='SimToReco Score'), 'simToRecoScore_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Score'), + 'simToRecoScore_EnSimTrack': dict(ytitle='SimTrack Energy [GeV]', var='# Clusters', xtitle='SimToReco Score'), 'simToRecoScore_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='SimToReco Score'), - 'simToRecoShEnF_En': dict(ytitle='Energy from SimTrack [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), - 'simToRecoShEnF_EnHits': dict(ytitle='Energy from hits [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_En': dict(ytitle='SimCluster Energy [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), 'simToRecoShEnF_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), + 'simToRecoShEnF_EnSimTrack': dict(ytitle='SimTrack Energy [GeV]', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), 'simToRecoShEnF_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='SimToReco Shared Energy Fraction'), 'simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# Clusters', xtitle='SimToReco Shared Energy Fraction', logz=True), } @@ -769,7 +760,7 @@ def __call__(self, parser, namespace, values, option_string=None): if args.ticl: sub_folder = 'TiclBarrel' else: sub_folder = 'ParticleFlow' - dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/CaloParticles_EnFracCut{str(args.EnFracCut).replace('.', 'p')}_PtCut{str(args.PtCut).replace('.', 'p')}" + dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/{matching}/CaloParticles" afile = ROOT.TFile.Open(args.file) checkRootDir(afile, dqm_dir) From 01b3d4ef3d61a09e9cfaf7281b9b940050bd4a98 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 10 Dec 2025 18:04:00 +0100 Subject: [PATCH 17/64] Generalise to EB and EE for Run-3 --- SimGeneral/MixingModule/python/caloTruthProducer_cfi.py | 1 + .../HGCalValidation/python/HLTHGCalValidator_cff.py | 3 ++- Validation/RecoParticleFlow/plugins/PFTester.cc | 8 +++++--- Validation/RecoParticleFlow/python/hltPFValidation_cfi.py | 5 +++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py index b110df69fce15..5969022364b0a 100644 --- a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py +++ b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py @@ -19,6 +19,7 @@ # hcal = cms.VInputTag(cms.InputTag('g4SimHits','HcalHits')), ecal = cms.VInputTag( cms.InputTag('g4SimHits','EcalHitsEB'), + cms.InputTag('g4SimHits','EcalHitsEE'), ) ), simTrackCollection = cms.InputTag('g4SimHits'), diff --git a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py index 349c6fd6f9a57..d6bff18ac851c 100644 --- a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py +++ b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py @@ -59,7 +59,8 @@ assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), - ptCut = cms.double(0.) + ptCut = cms.double(0.), + etaCut = cms.double(1.48) ) hltLayerClusterTesterECALWithCut = hltLayerClusterTesterECAL.clone( diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index ec6663de79ea4..4d5a8852c9a78 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -102,6 +102,7 @@ class PFTesterT : public DQMEDAnalyzer { uint nAssocScoreThresholds_; double enFracCut_; double ptCut_; + double etaCut_; bool doMatchByScore_; std::string outFolder_; @@ -193,6 +194,7 @@ PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) assocScoreThresholds_(iConfig.getParameter>("assocScoreThresholds")), enFracCut_(iConfig.getParameter("enFracCut")), ptCut_(iConfig.getParameter("ptCut")), + etaCut_(iConfig.getParameter("etaCut")), doMatchByScore_(iConfig.getParameter("doMatchByScore")), outFolder_(iConfig.getParameter("outFolder")) { nAssocScoreThresholds_ = assocScoreThresholds_.size(); @@ -670,7 +672,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e auto const scTrack = simClusters[simId].g4Tracks()[0]; const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); auto const simTrackEtaAtBoundary = pos.Eta(); - if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel + if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; ++nSimClusters; @@ -893,7 +895,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // tracker/calorimeter boundary outside the barrel auto const scTrack = simClusters[simPairIdx].g4Tracks()[0]; const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); - if (abs(pos.Eta()) > 1.48) // simTrack does not cross the barrel + if (abs(pos.Eta()) > etaCut_) // simTrack does not cross the barrel continue; h_recoToSimScore_->Fill(simPair.second); @@ -967,7 +969,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e auto const scTrack = simClusters[simId].g4Tracks()[0]; const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); auto const simTrackEtaAtBoundary = pos.Eta(); - if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel + if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; const edm::Ref simClusterRef(SimCluster, simId); diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index df5e85a413582..e51f344bfb48d 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -36,11 +36,12 @@ assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), - ptCut = cms.double(0.) + ptCut = cms.double(0.), + etaCut = cms.double(3.0) ) from Configuration.Eras.Modifier_phase2_common_cff import phase2_common -phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp")) +phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp"), etaCut = cms.double(1.48)) hltPFClusterTesterECALWithCut = hltPFClusterTesterECAL.clone( enFracCut = cms.double(0.01), From 985d6deb85c9436f46090cbf53ba0c21ab488f5e Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Mon, 3 Nov 2025 17:04:16 +0100 Subject: [PATCH 18/64] New RecoToSim and SimToReco score definition for LCToSC and LCToCP --- .../LCToCPAssociatorByEnergyScoreImpl.cc | 31 ++++++++++++++++--- .../LCToSCAssociatorByEnergyScoreImpl.cc | 31 ++++++++++++++++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc index 0eb1df76d3ec8..89b53904c6b5b 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc @@ -411,8 +411,20 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti if (findHitIt != detIdToCaloParticleId_Map[rh_detid].end()) cpFraction = findHitIt->fraction; } - cpPair.second += std::min(std::pow(rhFraction - cpFraction, 2), std::pow(rhFraction, 2)) * hitEnergyWeight * - invLayerClusterEnergyWeight; + // old score definition + // cpPair.second += std::min(std::pow(rhFraction - cpFraction, 2), std::pow(rhFraction, 2)) * hitEnergyWeight * + // invLayerClusterEnergyWeight; + // new score definition + /* RecoToSim score logic: + - recoFraction > 0 && simFraction == 0 : reco cluster contains non-sim associated elements : penalty in score by recoFraction*E + - recoFraction = 0 && simFraction >= 0 : simhits not present in reco cluster : ignore in RecoToSim + - recoFraction == 1 && simFraction == 1 : good association + - 1 > simFraction > recoFraction > 0 : we are missing some sim energy: consider as good association since what was collected is all good + - 1 > recoFraction > simFraction > 0 : we have collected too much energy : penalty in score by the part in eccess : (recoFraction-simFraction)*E + */ + float recoMinusSimFraction = std::max(0.f, rhFraction - cpFraction); + cpPair.second += recoMinusSimFraction * recoMinusSimFraction * hitEnergyWeight * invLayerClusterEnergyWeight; + } //End of loop over CaloParticles related the this LayerCluster. } // End of loop over Hits within a LayerCluster #ifdef EDM_ML_DEBUG @@ -487,8 +499,19 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti if (findHitIt != detIdToLayerClusterId_Map[cp_hitDetId].end()) lcFraction = findHitIt->fraction; } - lcPair.second.second += std::min(std::pow(lcFraction - cpFraction, 2), std::pow(cpFraction, 2)) * - hitEnergyWeight * invCPEnergyWeight; + // old score definition + // lcPair.second.second += std::min(std::pow(lcFraction - cpFraction, 2), std::pow(cpFraction, 2)) * + // hitEnergyWeight * invCPEnergyWeight; + // new score definition + /* SimToReco score logic: + - simFraction = 0 && recoFraction >= 0 : reco cluster contains non-sim associated elements : ignore in simToReco + - simFraction > 0 && recoFraction == 0 : simhits not present in reco cluster : penalty in score by simFraction*E + - simFraction == 1 && recoFraction == 1 : good association + - 1 > simFraction > recoFraction > 0 : we are missing some sim energy : penalty in score by the missing part (simFraction-recoFraction)*E + - 1 > recoFraction > simFraction > 0 : we have collected too much energy : consider as good association since all the sim was collected + */ + float simMinusRecoFraction = std::max(0.f, cpFraction - lcFraction); + lcPair.second.second += simMinusRecoFraction * simMinusRecoFraction * hitEnergyWeight * invCPEnergyWeight; #ifdef EDM_ML_DEBUG LogDebug("LCToCPAssociatorByEnergyScoreImplT") << "cpDetId:\t" << (uint32_t)cp_hitDetId << "\tlayerClusterId:\t" << layerClusterId << "\t" diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index e8ac95d8cc7fc..c2be3df503681 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -413,8 +413,20 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti if (findHitIt != detIdToSimClusterId_Map[rh_detid].end()) scFraction = findHitIt->fraction; } - scPair.second += std::min(std::pow(rhFraction - scFraction, 2), std::pow(rhFraction, 2)) * hitEnergyWeight * - invLayerClusterEnergyWeight; + // old score definition + // scPair.second += std::min(std::pow(rhFraction - scFraction, 2), std::pow(rhFraction, 2)) * hitEnergyWeight * + // invLayerClusterEnergyWeight; + // new score definition + /* RecoToSim score logic: + - recoFraction > 0 && simFraction == 0 : reco cluster contains non-sim associated elements : penalty in score by recoFraction*E + - recoFraction = 0 && simFraction >= 0 : simhits not present in reco cluster : ignore in RecoToSim + - recoFraction == 1 && simFraction == 1 : good association + - 1 > simFraction > recoFraction > 0 : we are missing some sim energy: consider as good association since what was collected is all good + - 1 > recoFraction > simFraction > 0 : we have collected too much energy : penalty in score by the part in eccess : (recoFraction-simFraction)*E + */ + float recoMinusSimFraction = std::max(0.f, rhFraction - scFraction); + scPair.second += recoMinusSimFraction * recoMinusSimFraction * hitEnergyWeight * invLayerClusterEnergyWeight; + #ifdef EDM_ML_DEBUG LogDebug("LCToSCAssociatorByEnergyScoreImplT") << "rh_detid:\t" << (uint32_t)rh_detid << "\tlayerClusterId:\t" << lcId << "\t" @@ -501,8 +513,19 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti if (findHitIt != detIdToLayerClusterId_Map[sc_hitDetId].end()) lcFraction = findHitIt->fraction; } - lcPair.second.second += std::min(std::pow(lcFraction - scFraction, 2), std::pow(scFraction, 2)) * - hitEnergyWeight * invSCEnergyWeight; + // old score definition + // lcPair.second.second += std::min(std::pow(lcFraction - scFraction, 2), std::pow(scFraction, 2)) * + // hitEnergyWeight * invSCEnergyWeight; + // new score definition + /* SimToReco score logic: + - simFraction = 0 && recoFraction >= 0 : reco cluster contains non-sim associated elements : ignore in simToReco + - simFraction > 0 && recoFraction == 0 : simhits not present in reco cluster : penalty in score by simFraction*E + - simFraction == 1 && recoFraction == 1 : good association + - 1 > simFraction > recoFraction > 0 : we are missing some sim energy : penalty in score by the missing part (simFraction-recoFraction)*E + - 1 > recoFraction > simFraction > 0 : we have collected too much energy : consider as good association since all the sim was collected + */ + float simMinusRecoFraction = std::max(0.f, scFraction - lcFraction); + lcPair.second.second += simMinusRecoFraction * simMinusRecoFraction * hitEnergyWeight * invSCEnergyWeight; #ifdef EDM_ML_DEBUG LogDebug("LCToSCAssociatorByEnergyScoreImplT") << "scDetId:\t" << (uint32_t)sc_hitDetId << "\tlayerClusterId:\t" << layerClusterId << "\t" From 2ed52fe1c6fa3510ee5911e3c6f93554dac9cb0e Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 25 Nov 2025 11:09:23 +0100 Subject: [PATCH 19/64] Event Display for ECAL barrel --- .../plugins/EcalGeometryAnalyzer.cc | 456 +++++++++++ .../scripts/showECALcrystals.py | 209 ++++++ .../scripts/showECALcrystals_interactive.py | 709 ++++++++++++++++++ Validation/RecoParticleFlow/scripts/utils.py | 92 +++ .../test/ecalGeometryAnalyzer_cfg.py | 155 ++++ 5 files changed, 1621 insertions(+) create mode 100644 Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc create mode 100755 Validation/RecoParticleFlow/scripts/showECALcrystals.py create mode 100755 Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py create mode 100644 Validation/RecoParticleFlow/scripts/utils.py create mode 100644 Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py diff --git a/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc b/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc new file mode 100644 index 0000000000000..07f548c660252 --- /dev/null +++ b/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc @@ -0,0 +1,456 @@ +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Framework/interface/one/EDAnalyzer.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "CommonTools/UtilAlgos/interface/TFileService.h" + +#include "Geometry/CaloGeometry/interface/CaloGeometry.h" +#include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" +#include "SimDataFormats/CaloAnalysis/interface/CaloParticleFwd.h" +#include "Geometry/CaloGeometry/interface/CaloSubdetectorGeometry.h" +#include "Geometry/CaloGeometry/interface/CaloCellGeometry.h" +#include "Geometry/Records/interface/CaloGeometryRecord.h" +#include "Geometry/CaloTopology/interface/CaloTopology.h" +#include "Geometry/CaloTopology/interface/EcalBarrelTopology.h" +#include "Geometry/CaloTopology/interface/EcalEndcapTopology.h" + +#include "Geometry/CaloGeometry/interface/EZArrayFL.h" + +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "CommonTools/UtilAlgos/interface/TFileService.h" + +#include "DataFormats/ParticleFlowReco/interface/PFRecHit.h" +#include "DataFormats/ParticleFlowReco/interface/PFRecHitFwd.h" +#include "SimDataFormats/CaloHit/interface/PCaloHit.h" +#include "DataFormats/ParticleFlowReco/interface/PFCluster.h" +#include "DataFormats/ParticleFlowReco/interface/PFClusterFwd.h" +#include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" +#include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" +#include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" +#include "SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h" + +#include +#include +#include "TTree.h" + +class EcalGeometryAnalyzer : public edm::one::EDAnalyzer { +public: + explicit EcalGeometryAnalyzer(const edm::ParameterSet&); + ~EcalGeometryAnalyzer() override {} + + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +private: + void beginJob() override; + void analyze(edm::Event const&, edm::EventSetup const&) override; + void endJob() override {} + double inBarrel(const DetId& id); + bool needsAssociator(bool kinCuts, double respCut); + + edm::ESGetToken caloGeomToken_; + edm::EDGetTokenT caloParticleToken_; + edm::EDGetTokenT recHitToken_; + edm::EDGetTokenT> simHitToken_; + edm::EDGetTokenT recClusterToken_; + edm::EDGetTokenT simClusterToken_; + edm::EDGetTokenT> RecoToSimAssociatorToken_; + edm::EDGetTokenT> SimToRecoAssociatorToken_; + + bool kinematicCuts_; + double enFracCut_; + double ptCut_; + double scoreCut_; + double responseCut_; + + TTree *geomTree_, *eventTree_; + + unsigned crystalDetId_; + float crystalCenterEta_; + float crystalCenterPhi_; + float crystalCorner0Eta_; + float crystalCorner1Eta_; + float crystalCorner2Eta_; + float crystalCorner3Eta_; + float crystalCorner0Phi_; + float crystalCorner1Phi_; + float crystalCorner2Phi_; + float crystalCorner3Phi_; + + static constexpr std::array prefixes_ = {{"Reco", "Sim"}}; + unsigned eventId_; + + template + using UMap = std::unordered_map; + + UMap nHits_; + UMap> detids_; + UMap> energies_; + + UMap> clusterEnergies_; + UMap> clusterEtas_; + UMap> clusterPhis_; + UMap> clusterHitDetids_; + UMap> clusterHitClids_; + UMap> clusterHitEnergies_; + UMap> clusterHitFractions_; +}; + +EcalGeometryAnalyzer::EcalGeometryAnalyzer(const edm::ParameterSet& iConfig) + : caloGeomToken_(esConsumes()), + caloParticleToken_(consumes(iConfig.getParameter("caloParticles"))), + recHitToken_(consumes(iConfig.getParameter("recHits"))), + simHitToken_(consumes>(iConfig.getParameter("simHits"))), + recClusterToken_(consumes(iConfig.getParameter("recClusters"))), + simClusterToken_(consumes(iConfig.getParameter("simClusters"))), + kinematicCuts_(iConfig.getUntrackedParameter("kinematicCuts")), + enFracCut_(iConfig.getUntrackedParameter("enFracCut")), + ptCut_(iConfig.getUntrackedParameter("ptCut")), + scoreCut_(iConfig.getUntrackedParameter("scoreCut")), + responseCut_(iConfig.getUntrackedParameter("responseCut")) +{ + edm::Service fs; + geomTree_ = fs->make("Geometry", "Geometry data"); + eventTree_ = fs->make("Event", "Event data"); + + assert(enFracCut_ >= 0.); + assert(ptCut_ >= 0.); + assert(scoreCut_ >= 0. && scoreCut_ <= 1.); + assert(responseCut_ >= 0.); + + // this cut avoids the need to have associator information in the event if + // it is not needed + if (needsAssociator(kinematicCuts_, responseCut_)) { + SimToRecoAssociatorToken_ = consumes>(iConfig.getParameter("clusterAssociator")); + } +} + +void EcalGeometryAnalyzer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("caloParticles", edm::InputTag("mix", "MergedCaloTruth")); + desc.add("recHits", edm::InputTag("hltParticleFlowRecHitECALUnseeded")); + desc.add("simHits", edm::InputTag("g4SimHits", "EcalHitsEB")); + desc.add("recClusters", edm::InputTag("hltParticleFlowClusterECALUnseeded")); + desc.add("simClusters", edm::InputTag("mix", "MergedCaloTruth")); + desc.addUntracked("kinematicCuts", false); + desc.addUntracked("enFracCut", 0.01); + desc.addUntracked("ptCut", 0.1); + desc.addUntracked("scoreCut", 1.); + desc.addUntracked("responseCut", 0.); + desc.addOptional("clusterAssociator", edm::InputTag("hltPFClusterSimClusterAssociationProducerECAL")); + descriptions.add("ecalGeometryAnalyzer", desc); +} + +void EcalGeometryAnalyzer::beginJob() { + geomTree_->Branch("crystalDetId", &crystalDetId_); + geomTree_->Branch("crystalCenterEta", &crystalCenterEta_); + geomTree_->Branch("crystalCenterPhi", &crystalCenterPhi_); + geomTree_->Branch("crystalCorner0Eta", &crystalCorner0Eta_); + geomTree_->Branch("crystalCorner1Eta", &crystalCorner1Eta_); + geomTree_->Branch("crystalCorner2Eta", &crystalCorner2Eta_); + geomTree_->Branch("crystalCorner3Eta", &crystalCorner3Eta_); + geomTree_->Branch("crystalCorner0Phi", &crystalCorner0Phi_); + geomTree_->Branch("crystalCorner1Phi", &crystalCorner1Phi_); + geomTree_->Branch("crystalCorner2Phi", &crystalCorner2Phi_); + geomTree_->Branch("crystalCorner3Phi", &crystalCorner3Phi_); + + eventTree_->Branch("eventId", &eventId_); + + for (auto& prefix : prefixes_) { + eventTree_->Branch(("nHits" + prefix).c_str(), &nHits_[prefix]); + eventTree_->Branch(("detids" + prefix).c_str(), &detids_[prefix]); + eventTree_->Branch(("energies" + prefix).c_str(), &energies_[prefix]); + + eventTree_->Branch(("clusterEnergies" + prefix).c_str(), &clusterEnergies_[prefix]); + eventTree_->Branch(("clusterEtas" + prefix).c_str(), &clusterEtas_[prefix]); + eventTree_->Branch(("clusterPhis" + prefix).c_str(), &clusterPhis_[prefix]); + eventTree_->Branch(("clusterHitDetids" + prefix).c_str(), &clusterHitDetids_[prefix]); + eventTree_->Branch(("clusterHitClids" + prefix).c_str(), &clusterHitClids_[prefix]); + eventTree_->Branch(("clusterHitEnergies" + prefix).c_str(), &clusterHitEnergies_[prefix]); + eventTree_->Branch(("clusterHitFractions" + prefix).c_str(), &clusterHitFractions_[prefix]); + } +} + +// check the detid lies in the ECAL barrel +double EcalGeometryAnalyzer::inBarrel(const DetId& id) { + return id.det() == DetId::Ecal && id.subdetId() == EcalBarrel; +} + +bool EcalGeometryAnalyzer::needsAssociator(bool kinCuts, double respCut) { + return kinCuts || respCut > 0.; +} + +void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { + // Get the ECAL geometry + const auto& caloGeom = iSetup.getData(caloGeomToken_); + const auto& barrelGeom = caloGeom.getSubdetectorGeometry(DetId::Ecal, EcalBarrel); + const std::vector detids = barrelGeom->getValidDetIds(); + + // Reset vector variables + for (auto& prefix : prefixes_) { + detids_[prefix].clear(); + energies_[prefix].clear(); + + clusterEnergies_[prefix].clear(); + clusterEtas_[prefix].clear(); + clusterPhis_[prefix].clear(); + clusterHitEnergies_[prefix].clear(); + clusterHitFractions_[prefix].clear(); + clusterHitDetids_[prefix].clear(); + clusterHitClids_[prefix].clear(); + } + + unsigned eventId = iEvent.id().event(); + eventId_ = eventId; + + // Geometry fill + if (eventId == 1) { + for (auto& did : detids) { + if (did.subdetId() != EcalBarrel) { + throw std::runtime_error("Error"); + continue; + } + + const CaloCellGeometry* cellGeom = barrelGeom->getGeometry(did); + crystalDetId_ = did.rawId(); + crystalCenterEta_ = cellGeom->getPosition().eta(); + crystalCenterPhi_ = cellGeom->getPosition().phi(); + + const EZArrayFL corners = cellGeom->getCorners(); + crystalCorner0Eta_ = corners[0].eta(); + crystalCorner1Eta_ = corners[1].eta(); + crystalCorner2Eta_ = corners[2].eta(); + crystalCorner3Eta_ = corners[3].eta(); + crystalCorner0Phi_ = corners[0].phi(); + crystalCorner1Phi_ = corners[1].phi(); + crystalCorner2Phi_ = corners[2].phi(); + crystalCorner3Phi_ = corners[3].phi(); + + geomTree_->Fill(); + } + } // if (eventId == 1) + + edm::Handle recHits_; + iEvent.getByToken(recHitToken_, recHits_); + if (!recHits_.isValid()) { + edm::LogInfo("EcalGeometryAnalyzer") << "Input recHit collection not found."; + return; + } + edm::Handle> simHits_; + iEvent.getByToken(simHitToken_, simHits_); + if (!simHits_.isValid()) { + edm::LogInfo("EcalGeometryAnalyzer") << "Input simHit collection not found."; + return; + } + edm::Handle recClusters_; + iEvent.getByToken(recClusterToken_, recClusters_); + if (!recClusters_.isValid()) { + edm::LogInfo("EcalGeometryAnalyzer") << "Input recCluster collection not found."; + return; + } + edm::Handle simClusters_; + iEvent.getByToken(simClusterToken_, simClusters_); + if (!simClusters_.isValid()) { + edm::LogInfo("EcalGeometryAnalyzer") << "Input simCluster collection not found."; + return; + } + edm::Handle caloParticles_; + iEvent.getByToken(caloParticleToken_, caloParticles_); + if (!caloParticles_.isValid()) { + edm::LogPrint("EcalGeometryAnalyzer") << "Input CaloParticle collection not found."; + return; + } + + auto caloParticles = *caloParticles_; + auto recHits = *recHits_; + auto simHits = *simHits_; + auto recClusters = *recClusters_; + auto simClusters = *simClusters_; + + // Sim to Reco associator + edm::Handle> SimToRecoAssociatorCollection; + ticl::SimToRecoCollectionWithSimClustersT simToRecoAssoc; + if (needsAssociator(kinematicCuts_, responseCut_)) { + iEvent.getByToken(SimToRecoAssociatorToken_, SimToRecoAssociatorCollection); + if (!SimToRecoAssociatorCollection.isValid()) { + edm::LogPrint("EcalGeometryAnalyzer") << "Input clusterAssociator SimToReco collection not found."; + return; + } + simToRecoAssoc = *SimToRecoAssociatorCollection; + } + else { + simToRecoAssoc = ticl::SimToRecoCollectionWithSimClustersT(); + } + + // Build map linking each sim cluster to the energy of their mother calo particle + std::unordered_map simClusterToCPEnergyMap; + for (unsigned int cpId = 0; cpId < caloParticles.size(); ++cpId) { + // Fill map: for each simCluster, the energy of the caloParticle computed as the sum of all simClusters arising from it + double energySumSimHits = 0; + for (const auto& scRef : caloParticles[cpId].simClusters()) { + auto const& sc = *(scRef); + for (auto hit_energy : sc.hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + } + for (const auto& scRef : caloParticles[cpId].simClusters()) { + simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; + } + } + + // Event fill before any cuts + nHits_["Reco"] = recHits.size(); + for (auto& rechit : recHits) { + DetId id(rechit.detId()); + if (!inBarrel(id)) + continue; + detids_["Reco"].push_back(rechit.detId()); + energies_["Reco"].push_back(rechit.energy()); + } + + nHits_["Sim"] = simHits.size(); + for (auto& simhit : simHits) { + DetId id(simhit.id()); + if (!inBarrel(id)) + continue; + detids_["Sim"].push_back(simhit.id()); + energies_["Sim"].push_back(simhit.energy()); + } + + // reco clusters + unsigned recClusterCounter = 0; + for (auto& rcl : recClusters) { + // properties of the clusters + clusterEnergies_["Reco"].push_back(rcl.energy()); + clusterEtas_["Reco"].push_back(rcl.eta()); + clusterPhis_["Reco"].push_back(rcl.phi()); + + recClusterCounter++; + + for (auto const& rechit : rcl.recHitFractions()) { + const auto& ref = rechit.recHitRef(); + DetId clhitId(ref->detId()); + if (!inBarrel(clhitId)) + continue; + + clusterHitDetids_["Reco"].push_back(clhitId); + clusterHitClids_["Reco"].push_back(recClusterCounter); + clusterHitEnergies_["Reco"].push_back(ref->energy()); + clusterHitFractions_["Reco"].push_back(rechit.fraction()); + } + } + + // sim clusters + + /* remove the event if no sim cluster is matched to a reco cluster + with a response higher than "responseCut" + */ + bool passResponseMatch = false; + if (needsAssociator(kinematicCuts_, responseCut_)) { + for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + const edm::Ref simClusterRef(simClusters_, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + + for (const auto& recoPair : simToRecoMatched) { + auto recoId = recoPair.first.index(); + double response = recClusters[recoId].energy() / simClusters[simId].energy(); + if (response >= responseCut_) { + passResponseMatch = true; + break; + } + } + + if (passResponseMatch) + break; + } + } + else { + passResponseMatch = true; + } + + unsigned simClusterCounter = 0; + for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + if (!passResponseMatch) + break; + + auto& scl = simClusters[simId]; + if (kinematicCuts_) + { + double energySumSimHits = 0; + for (auto hit_energy : scl.hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + + // apply cut on energy fraction + // (sim cluster energy wrt all sim clusters from same calo particle) + double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; + if (SimClusterToCPEnergyFraction < enFracCut_) + continue; + // apply cut on pt of the sim track + if (simClusters[simId].pt() < ptCut_) + continue; + + // filter all sim clusters produced by a sim track which crossed the + // tracker/calorimeter boundary outside the barrel + auto const scTrack = simClusters[simId].g4Tracks()[0]; + const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + auto const simTrackEtaAtBoundary = pos.Eta(); + if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel + continue; + + const edm::Ref simClusterRef(simClusters_, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + + // remove the cluster (not the event!) + // if the sim cluster is not matched to a reco cluster + // with a score lower than "scoreCut" + bool passScoreMatch = false; + for (const auto& recoPair : simToRecoMatched) { + if (recoPair.second.second <= scoreCut_) { + passScoreMatch = true; + break; + } + } + if (!passScoreMatch) + continue; + } + + // properties of the clusters + clusterEnergies_["Sim"].push_back(scl.energy()); + clusterEtas_["Sim"].push_back(scl.eta()); + clusterPhis_["Sim"].push_back(scl.phi()); + + simClusterCounter++; + + // properties of the hits in each cluster + const auto& hits_fractions = scl.hits_and_fractions(); + const auto& hits_energies = scl.hits_and_energies(); + + auto itF = hits_fractions.begin(); + auto itE = hits_energies.begin(); + for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { + DetId clhitId(itF->first); + if (!inBarrel(clhitId)) + continue; + clusterHitDetids_["Sim"].push_back(clhitId); + clusterHitClids_["Sim"].push_back(simClusterCounter); + clusterHitEnergies_["Sim"].push_back(itE->second); + clusterHitFractions_["Sim"].push_back(itF->second); + } + } + + eventTree_->Fill(); +} + +DEFINE_FWK_MODULE(EcalGeometryAnalyzer); diff --git a/Validation/RecoParticleFlow/scripts/showECALcrystals.py b/Validation/RecoParticleFlow/scripts/showECALcrystals.py new file mode 100755 index 0000000000000..6416ad9f706ee --- /dev/null +++ b/Validation/RecoParticleFlow/scripts/showECALcrystals.py @@ -0,0 +1,209 @@ +import os +import argparse +import uproot +import utils +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.patches as patches +import matplotlib.collections as coll +from matplotlib.ticker import MultipleLocator +import matplotlib.cm as cm +import matplotlib.colors as mcolors +import mplhep as hep +import pandas as pd +hep.style.use("CMS") +from dataclasses import dataclass + +def plotGeom(df): + fig, ax = plt.subplots() + params = dict(ax=ax, fontsize=15) + hep.cms.text(' Phase-2 Simulation Preliminary', **params) + hep.cms.lumitext("ECAL Geometry", **params) + + ax.scatter(df.crystalCenterEta, df.crystalCenterPhi, c='red', s=1) + + pat = [] + for row in df.itertuples(index=False): + width = utils.angleDiff(row.crystalCorner2Eta, row.crystalCorner0Eta) + height = utils.angleDiff(row.crystalCorner2Phi, row.crystalCorner0Phi) + sq = patches.Rectangle((row.crystalCorner2Eta, row.crystalCorner2Phi), + width, height, + edgecolor='black', fill=False) + pat.append(sq) + ax.add_collection(coll.PatchCollection(pat, facecolor='none', edgecolor='black')) + + ax.scatter(df.crystalCenterEta, df.crystalCenterPhi, c='red', s=1) + + # ax.xaxis.set_major_locator(MultipleLocator(0.01)) + # ax.xaxis.set_minor_locator(MultipleLocator(0.05)) + # ax.yaxis.set_major_locator(MultipleLocator(0.02)) + # ax.yaxis.set_minor_locator(MultipleLocator(0.005)) + + plt.xlabel(f'$\eta$') + plt.ylabel(f'$\phi$') + plt.grid(True, which='both', linestyle='--', linewidth=0.5) + plt.tight_layout() + fig.savefig('test.pdf') + plt.close() + +def add_black(cmap): + """Add black at the end of a cmap.""" + colors = cmap(np.linspace(0, 1, cmap.N)) + new_colors = np.vstack((colors, [0, 0, 0, 1])) # add black + return mcolors.ListedColormap(new_colors) + +def plotEvent(geom, hits, clusters, out, zoom, var='energy', zlabel='', categorical=False): + """ + Plot single event on top of the geometry. + """ + pardir = utils.getParentDir(out) + utils.createDir(pardir) + utils.createIndexPHP(src=utils.getParentDir(pardir), dest=pardir) + + df = pd.merge(hits, geom, how='inner', left_on='detid', right_on='crystalDetId') + # df = df[df[var]>0] + # df[df.duplicated(subset='detid', keep=False) == True] + + fig, ax = plt.subplots() + params = dict(ax=ax, fontsize=15) + hep.cms.text(' Phase-2 Simulation Preliminary', **params) + hep.cms.lumitext("CloseByElectron | 14 TeV", **params) + + if categorical: + cmap = add_black(plt.get_cmap('tab10')) + norm = mcolors.Normalize(vmin=0, vmax=len(cmap.colors)) + colors = [cm.ScalarMappable(norm=norm, cmap=cmap).to_rgba(x-1) for x in df.clid] + colors = [(r, g, b, 0.6) for (r, g, b, a) in colors] # change opacity + else: + cmap = cm.viridis + # norm = mcolors.Normalize(vmin=df[var].min(), vmax=df[var].max()) + norm = mcolors.LogNorm(vmin=df[var].min(), vmax=df[var].max()) + colors = cmap(norm(df[var].values)) + + # Add rectangles + pat = [] + for row in df.itertuples(index=False): + width = utils.angleDiff(row.crystalCorner2Eta, row.crystalCorner0Eta) + height = utils.angleDiff(row.crystalCorner2Phi, row.crystalCorner0Phi) + sq = patches.Rectangle((row.crystalCorner2Eta, row.crystalCorner2Phi), + width, height) + pat.append(sq) + + ax.add_collection(coll.PatchCollection(pat, facecolor=colors, edgecolor='black')) + + # Add colorbar + if not categorical: + sm = cm.ScalarMappable(cmap=cmap, norm=norm) + sm.set_array([]) + fig.colorbar(sm, ax=ax, label=zlabel) + + plt.scatter(x=clusters.clusterEta, y=clusters.clusterPhi, + color='red', marker='x', s=100, linewidth=4, + label='Clusters') + + plt.xlabel(f'$\eta$') + plt.ylabel(f'$\phi$') + if zoom: + ax.set_xlim(zoom[0], zoom[1]) + ax.set_ylim(zoom[2], zoom[3]) + else: + ax.set_xlim(-1.5, 1.5) + ax.set_ylim(-3.15, 3.15) + + plt.grid(True, which='both', linestyle='--', linewidth=0.5) + plt.tight_layout() + extensions = ('.pdf', '.png') + for ext in extensions: + fig.savefig(out + ext) + print(f"INFO: Figure saved under {out}{'/'.join(extensions)}") + plt.close() + +def showECAL(infile, outfile, props): + varsGeom = ['crystalDetId', 'crystalCenterEta', 'crystalCenterPhi', + 'crystalCorner0Eta', 'crystalCorner1Eta', 'crystalCorner2Eta', 'crystalCorner3Eta', + 'crystalCorner0Phi', 'crystalCorner1Phi', 'crystalCorner2Phi', 'crystalCorner3Phi'] + + varsEventCommon = ['eventId'] + varsEvent = {"Reco": [], "Sim": []} + for prefix in ("Reco", "Sim"): + varsEvent[prefix].extend([x+prefix for x in + ('energies', 'detids', 'nHits', + 'clusterEnergies', 'clusterEtas', 'clusterPhis', + 'clusterHitEnergies', 'clusterHitFractions', + 'clusterHitClids', 'clusterHitDetids')]) + varsEventAll = varsEventCommon + varsEvent['Reco'] + varsEvent['Sim'] + + with uproot.open(infile) as file: + dfGeom = file['ecalGeometryAnalyzer/Geometry'].arrays(varsGeom, library="pandas") + dfEvent = file['ecalGeometryAnalyzer/Event'].arrays(varsEventAll, library="awkward") + + # filters + # if zoom: + # filt = lambda x : ((x.crystalCenterEta > 0.18) & (x.crystalCenterEta < 0.3) + # & (x.crystalCenterPhi > 0.1) & (x.crystalCenterPhi < 0.3)) + # else: + # filt = lambda x : x + + # dfGeom = dfGeom[filt(dfGeom)] + plotGeom(dfGeom) + + for t in dfEvent.eventId[:props.nevents]: + + for prefix in ("Reco", "Sim"): + print(f'INFO: Processing event {t} in mode {prefix}...') + + outname = "event" + prefix + str(t) + if props.zoom: + outname += "_zoom" + + dfEventTmp = dfEvent[dfEvent.eventId==t] + + # if prefix == "Sim": # energy cut to reduce hit multiplicity in plot + # en_mask = dfEventTmp.energiesSim > 0.0 + # dfEventTmp['energies'+prefix] = dfEventTmp['energies'+prefix][en_mask] + # dfEventTmp['detids'+prefix] = dfEventTmp['detids'+prefix][en_mask] + + dfHits = pd.DataFrame({'energy': dfEventTmp['energies'+prefix][0], + 'detid': dfEventTmp['detids'+prefix][0]}) + + dfClusters = pd.DataFrame({'clusterEnergy': dfEventTmp['clusterEnergies'+prefix][0], + 'clusterEta': dfEventTmp['clusterEtas'+prefix][0], + 'clusterPhi': dfEventTmp['clusterPhis'+prefix][0], + }) + + dfHitsInClusters = pd.DataFrame({'energy': dfEventTmp['clusterHitEnergies'+prefix][0], + 'frac': dfEventTmp['clusterHitFractions'+prefix][0], + 'detid': dfEventTmp['clusterHitDetids'+prefix][0], + 'clid': dfEventTmp['clusterHitClids'+prefix][0], + }) + + plotEvent(dfGeom, dfHits, dfClusters, + out=os.path.join(outfile, outname + '_allhits'), + var='energy', zoom=props.zoom, + zlabel=('PF' if prefix=='Reco' else 'Sim') + ' RecHit Energy [GeV]') + plotEvent(dfGeom, dfHitsInClusters, dfClusters, + out=os.path.join(outfile, outname + '_clhits'), + var='frac', zoom=props.zoom, categorical=False, + zlabel=('PF' if prefix=='Reco' else 'Sim') + ' RecHit Energy [GeV]') + + print('INFO: Done.') + + +@dataclass +class InputArgs: + zoom: bool + nevents: int + +if __name__ == '__main__': + + full_command = 'python3 Validation/RecoParticleFlow/scripts/showECALcrystals.py --file ' + parser = argparse.ArgumentParser(description='Show position of crystals. \nExample command:\n' + full_command) + + parser.add_argument('-i', '--file', help='Path to the input ROOT file.') + parser.add_argument('-o', '--outdir', help='Path to the output folder where the events will be stored.') + parser.add_argument('-z', '--zoom', help='Zoom over a hard-coded eta/phi region: (min_eta, max_eta, min_phi, max_phi)', nargs=4, type=float, default=None) + parser.add_argument('-n', '--nevents', help='Number of events to plot.', default=6) + + args = parser.parse_args() + props = InputArgs(zoom=args.zoom, nevents=args.nevents) + showECAL(args.file, args.outdir, props) diff --git a/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py b/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py new file mode 100755 index 0000000000000..8801c7bb6e683 --- /dev/null +++ b/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py @@ -0,0 +1,709 @@ +""" +If not available by default, to get bokeh on lxplus: + $ cmsenv + $ scram-venv + $ python3 -m pip install bokeh +""" + +import os +import argparse +import uproot +import awkward as ak +import utils +import numpy as np +import pandas as pd +import utils +from dataclasses import dataclass + +from bokeh.plotting import figure, output_file, save, ColumnDataSource +from bokeh.models import (HoverTool, Rect, ColumnDataSource, LinearColorMapper, LogColorMapper, + Div, Button, ColorBar, NumericInput, Dropdown, CDSView, GroupFilter, + BooleanFilter, CustomJS, Slider) +from bokeh.palettes import Viridis256, Category20 +from bokeh.transform import linear_cmap, log_cmap +from bokeh.layouts import layout + +def writeIntructions(): + text = """ + Interactive event display + +
+ +

+ This event display was developed to enable a flexible visualization of PF clusters in ECAL. + Its original goal was to facilitate PF validation at HLT. +

+ +

+ This page can be created by running: + +
+ +

+    python3 Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py -i data.root --outdir . --nevents 10
+    
+ within a CMSSW release. + Check all options by adding "--help". + +

+ The input "data.root" file holds the geometry and event information, and can be in turn produced with: + +
+ +

+    cmsRun Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py input="your step2 file".root output=data.root maxEvents=100
+    
+ where the input file refers to a CMS step2 file, where cluster information and ECAL geometry is available. +

+ +
+ + Capabilites + +

+ This tool enables the comparison of simulated (left) and reconstructed (right) hits and clusters. Specifically, you can: + +
+ +

    +
  • Visualize different events by changing the event number in "Event ID selection"
  • +
  • Filter crystals based on the deposited energy
  • +
  • Select specific clusters by their ID
  • +
  • Use selection tools on all figures simultaneously, available at the right of each plot (zoom, undo, ...)
  • +
+

+ +

+ The energy displayed corresponds to the total energy deposited in a given crystal. + Hover the crystals with your mouse to inspect the contributions of individual clusters. +

+

+ Energy fractions in a given crystal are available mostly as a debugging tool: we expect "FracSum" to be one for all crystals. +

+ +
+ +

+ Note: You might need to click twice on the "Show all clusters" buttons for them to work correctly. +

+ +
+ """ + return Div(text=text) + +def writeContacts(): + text = """ +
+

+ Tool developed under the Next Generation Trigger project (task 3.1.1). +

+

+ Contact: For bug reports or feature requests please reach out to bruno.alves@cern.ch. +

+
+ """ + return Div(text=text) + +def createFigure(title): + fig = figure( + title=title, + x_range=(-1.69,1.69), + y_range=(-1.05*np.pi,1.05*np.pi), + x_axis_label=r"$$\eta$$", + y_axis_label=r"$$\phi$$", + width=1100, + height=700, + tools="pan,wheel_zoom,box_zoom,undo,redo,reset,save", + active_drag="box_zoom", + active_scroll=None, + ) + fig.xgrid.grid_line_color = None + fig.ygrid.grid_line_color = None + fig.toolbar.logo = None + return fig + +def plotGeom(df, output_path): + """Plot ECAL geometry as interactive Bokeh plot.""" + output_file(output_path) + + df['width'] = utils.angleDiff(df.crystalCorner2Eta, df.crystalCorner0Eta) + df['height'] = utils.angleDiff(df.crystalCorner2Phi, df.crystalCorner0Phi) + # df = df.iloc[0:20000] + source = ColumnDataSource(df) + + # Create figure + p = createFigure(title='ECAL-Barrel Geometry') + + # Add rectangles for each crystal + p.add_tools(HoverTool( + tooltips=[ + ("DetID", "@crystalDetId"), + ("Center (η, φ)", "(@crystalCenterEta, @crystalCenterPhi)"), + ], + mode="mouse", + )) + + # Use rect glyph for each crystal + p.rect( + x="crystalCenterEta", + y="crystalCenterPhi", + width="width", + height="height", + source=source, + fill_color="lightgray", + line_color="black", + alpha=0.1, + ) + + # Add scatter for centers + p.scatter( + x="crystalCenterEta", + y="crystalCenterPhi", + source=source, + color="red", + size=6, + ) + p.scatter( + x="crystalCorner2Eta", + y="crystalCorner2Phi", + source=source, + color="green", + size=6, + ) + p.scatter( + x="crystalCorner0Eta", + y="crystalCorner0Phi", + source=source, + color="blue", + size=6, + ) + + save(p) + print(f"INFO: Geometry plot saved to {output_path}") + +def shift_phi_corners(phi0, phi1, phi2, phi3): + corners = [phi0, phi1, phi2, phi3] + # Check each pair of adjacent corners + for i in range(4): + j = (i + 1) % 4 + diff = abs(corners[i] - corners[j]) + if diff > np.pi: + # Shift the larger value by -2π + if corners[i] > corners[j]: + corners[i] -= 2 * np.pi + else: + corners[j] -= 2 * np.pi + return tuple(corners) + (corners[0],) # Close the patch + +def plotEvent(geom, hits, clusters, output_path): + """Plot single event on top of the geometry, with interactive hover.""" + output_file(output_path) + + modes = ('Sim', 'Reco') + eventDefault = 1 + p, df, hover, mapper_log, mapper_lin, color_bar, threshFilter, threshFilterId = ({} for _ in range(8)) + src, srcCluster, view, viewId, viewCluster, = ({} for _ in range (5)) + hit_renderer, cluster_renderer = ({} for _ in range(2)) + for mode in modes: + df[mode] = pd.merge(hits[mode], geom, how="inner", left_on="detids", right_on="crystalDetId") + + fracs_in_df = 'fracs' in df[mode].columns + clids_in_df = 'clids' in df[mode].columns + + df[mode]["eventId"] = df[mode]["eventId"].astype(str) + if clids_in_df: + df[mode]["clids"] = df[mode]["clids"].astype(str) + + view[mode] = CDSView(filters=[ + GroupFilter(column_name="eventId", group=str(eventDefault)), + BooleanFilter([True] * len(df[mode]))] + ) + threshFilter[mode] = view[mode].filters[1] + + if clids_in_df: + viewId[mode] = CDSView(filters=[ + GroupFilter(column_name="eventId", group=str(eventDefault)), + BooleanFilter([True] * len(df[mode])), + BooleanFilter([True] * len(df[mode]))] + ) + threshFilterId[mode] = viewId[mode].filters[2] + + clusters[mode]["eventId"] = clusters[mode]["eventId"].astype(str) + srcCluster[mode] = ColumnDataSource(clusters[mode]) + + viewCluster[mode] = CDSView(filters=[ + GroupFilter(column_name="eventId", group=str(eventDefault)),] + ) + + # Create lists of lists for xs and ys + df[mode]['xs'] = [ + (eta1, eta2, eta3, eta4, eta1) # Close the patch by repeating the first point + for eta1, eta2, eta3, eta4 in zip( + df[mode]["crystalCorner0Eta"], df[mode]["crystalCorner1Eta"], + df[mode]["crystalCorner2Eta"], df[mode]["crystalCorner3Eta"] + ) + ] + df[mode]['ys'] = [ + shift_phi_corners(phi0, phi1, phi2, phi3) # Close the patch by repeating the first point + for phi0, phi1, phi2, phi3 in zip( + df[mode]["crystalCorner0Phi"], df[mode]["crystalCorner1Phi"], + df[mode]["crystalCorner2Phi"], df[mode]["crystalCorner3Phi"] + ) + ] + + # Aggregate data for unique patches + patch_dict = { + 'eventId': df[mode]['eventId'], + 'xs': df[mode]['xs'], + 'ys': df[mode]['ys'], + 'energies': df[mode]['energies'], + } + aggr_dict = {'energies': 'sum'} + if fracs_in_df: + patch_dict.update({'fracs': df[mode]['fracs']}) + aggr_dict.update({'fracs': 'sum'}) + patch_data = pd.DataFrame(patch_dict) + aggr = patch_data.groupby(['eventId', 'xs', 'ys'], as_index=False).agg(aggr_dict) + + # Add summed variables to the original DataFrame + energy_sum_map = aggr.set_index(['eventId', 'xs', 'ys'])['energies'].to_dict() + df[mode]['energies_sum'] = df[mode].apply( + lambda row: energy_sum_map.get((row['eventId'], row['xs'], row['ys']), None), + axis=1 + ) + if fracs_in_df: + frac_sum_map = aggr.set_index(['eventId', 'xs', 'ys'])['fracs'].to_dict() + df[mode]['fracs_sum'] = df[mode].apply( + lambda row: frac_sum_map.get((row['eventId'], row['xs'], row['ys']), None), + axis=1 + ) + + src[mode] = ColumnDataSource(df[mode]) + + # Create figure + p[mode] = [createFigure(title=mode + " Hits")] + if clids_in_df: + p[mode].append(createFigure(title=mode + " Cluster IDs")) + + hit_renderer[mode] = [] + if clids_in_df: + # categorical figures + colors = [Category20[20][int(i) % 20] for i in df[mode].clids] + src[mode].add(colors, "colors") + hit_renderer[mode].append( + p[mode][1].patches( + xs="xs", ys="ys", + source=src[mode], + view=viewId[mode], + fill_color="colors", + line_color="black", + fill_alpha=0.5, + ) + ) + + # continuous figures + mapper_kwargs = dict(palette=Viridis256, low=df[mode]['energies_sum'].min(), high=df[mode]['energies_sum'].max()) + mapper_log[mode] = LogColorMapper(**mapper_kwargs) + mapper_lin[mode] = LinearColorMapper(**mapper_kwargs) + color_bar[mode] = ColorBar(color_mapper=mapper_log[mode], label_standoff=12) + + hit_renderer[mode].append( + p[mode][0].patches( + xs="xs", ys="ys", + source=src[mode], + view=view[mode], + fill_color=log_cmap('energies_sum', Viridis256, df[mode]['energies_sum'].min(), df[mode]['energies_sum'].max()), + line_color="black" + ) + ) + + p[mode][0].add_layout(color_bar[mode], "right") + + # Add hover tool + hover_string = '' + if clids_in_df: + hover_string += 'ClusterID: @clids, ' + if fracs_in_df: + hover_string += "Frac: @fracs{0.000}, FracSum: @fracs_sum{0.000}, En: @energies, EnSum: @energies_sum" + else: + hover_string += "En: @energies, EnSum: @energies_sum" + hover[mode] = HoverTool( # first string is the text + renderers=hit_renderer[mode], + tooltips=[ ("", hover_string), ], + mode="mouse", + ) + + for idx in range(len(p[mode])): + p[mode][idx].add_tools(hover[mode]) + + # Add clusters to the energy/fraction plot + if props.clusters: + cluster_renderer[mode] = p[mode][0].scatter( + x='clusterEtas' + mode, + y='clusterPhis' + mode, + source=srcCluster[mode], + view=viewCluster[mode], + color="red", + marker="x", + size=20, + line_width=4, + legend_label="Clusters", + ) + p[mode][0].legend.label_text_font_size = '16pt' + + cluster_hover_string = "Cluster Energy: @clusterEnergies" + mode + p[mode][0].add_tools( + HoverTool(renderers=[cluster_renderer[mode]], + tooltips=[("", cluster_hover_string), ])) + + p['Sim'][0].x_range, p['Sim'][0].y_range = p['Reco'][0].x_range, p['Reco'][0].y_range + if clids_in_df: + p['Sim'][1].x_range, p['Sim'][1].y_range = p['Reco'][0].x_range, p['Reco'][0].y_range + p['Reco'][1].x_range, p['Reco'][1].y_range = p['Reco'][0].x_range, p['Reco'][0].y_range + + enSumMax = 2. + slider = Slider(start=0, end=enSumMax, value=0.1, step=0.01, title="Min threshold for energies_sum", width=800) + + varNameHolder = ColumnDataSource(data=dict(value=["energies_sum"])) + + dfMin = min(df[mode].eventId.min() for mode in modes) + dfMax = max(df[mode].eventId.max() for mode in modes) + + numInput = NumericInput(value=eventDefault, low=int(dfMin), high=int(dfMax), + title=f"Event ID selection (enter a number between {dfMin} and {dfMax}):") + numInput_args = dict( + srcSim=src["Sim"], srcReco=src["Reco"], + srcClSim=srcCluster["Sim"], srcClReco=srcCluster["Reco"], + viewEvSim=view["Sim"], viewEvReco=view["Reco"], + viewClSim=viewCluster["Sim"], viewClReco=viewCluster["Reco"], + select=numInput, slider=slider, + threshSim=threshFilter["Sim"], threshReco=threshFilter["Reco"], + varNameHolder=varNameHolder, + ) + if clids_in_df: + numInput_args.update({'viewIdSim': viewId["Sim"], 'viewIdReco': viewId["Reco"],}) + numInput_code = """ + const eid = select.value.toString(); + viewEvSim.filters[0].group = eid; + viewEvReco.filters[0].group = eid; + viewIdSim.filters[0].group = eid; + viewIdReco.filters[0].group = eid; + viewClSim.filters[0].group = eid; + viewClReco.filters[0].group = eid; + + const minVal = slider.value; + const v = varNameHolder.data['value'][0]; + + const sim = srcSim.data; + const rec = srcReco.data; + + let maskSim = []; + let maskRec = []; + + for (let i = 0; i < sim[v].length; i++) { + maskSim.push(sim[v][i] >= minVal && sim["eventId"][i] === eid); + } + for (let i = 0; i < rec[v].length; i++) { + maskRec.push(rec[v][i] >= minVal && rec["eventId"][i] === eid); + } + + threshSim.booleans = maskSim; + threshReco.booleans = maskRec; + + srcSim.change.emit(); + srcReco.change.emit(); + srcClSim.change.emit(); + srcClReco.change.emit(); + viewEvSim.change.emit(); + viewEvReco.change.emit(); + viewIdSim.change.emit(); + viewIdReco.change.emit(); + viewClSim.change.emit(); + viewClReco.change.emit(); + """ + else: + numInput_code = """ + const eid = select.value.toString(); + viewEvSim.filters[0].group = eid; + viewEvReco.filters[0].group = eid; + viewClSim.filters[0].group = eid; + viewClReco.filters[0].group = eid; + + srcSim.change.emit(); + srcReco.change.emit(); + srcClSim.change.emit(); + srcClReco.change.emit(); + viewEvSim.change.emit(); + viewEvReco.change.emit(); + viewClSim.change.emit(); + viewClReco.change.emit(); + """ + + numInput_callb = CustomJS(args=numInput_args, code=numInput_code) + numInput.js_on_change("value", numInput_callb) + + title_template_one = f"Only one cluster available." + title_template_more = "Cluster ID selection (enter a number between {} and {}):" + if clids_in_df: + dfClIdSimMin, dfClIdSimMax = df['Sim'].clids.min(), df['Sim'].clids.max() + if dfClIdSimMin == dfClIdSimMax: + title = title_template_one + else: + title = title_template_more.format(dfClIdSimMin, dfClIdSimMax) + clIdInputSim = NumericInput(value=None, low=int(dfClIdSimMin), high=int(dfClIdSimMax), + title=title) + dfClIdRecoMin, dfClIdRecoMax = df['Reco'].clids.min(), df['Reco'].clids.max() + if dfClIdRecoMin == dfClIdRecoMax: + title = title_template_one + else: + title = title_template_more.format(dfClIdRecoMin, dfClIdRecoMax) + clIdInputReco = NumericInput(value=None, low=int(dfClIdRecoMin), high=int(dfClIdRecoMax), + title=title) + + clIdInput_code = """ + const eid = selectEvent.value.toString(); + const clid = selectId.value.toString(); + view.filters[0].group = eid; + + const clids = src.data.clids; + const eventIds = src.data.eventId; + const mask = clids.map((cid, i) => eventIds[i] === eid && cid === clid); + view.filters[1].booleans = mask; + + src.change.emit(); + view.change.emit(); + """ + clIdInputSim_callb = CustomJS(args=dict( + src=src['Sim'], view=viewId['Sim'], + selectEvent=numInput, selectId=clIdInputSim, + ), code=clIdInput_code) + clIdInputSim.js_on_change("value", clIdInputSim_callb) + + clIdInputReco_callb = CustomJS(args=dict( + src=src['Reco'], view=viewId['Reco'], + selectEvent=numInput, selectId=clIdInputReco, + ), code=clIdInput_code) + clIdInputReco.js_on_change("value", clIdInputReco_callb) + + showAllButtonSim = Button(label="Show all clusters", button_type="success", width=150) + showAllButtonReco = Button(label="Show all clusters", button_type="success", width=150) + + showAll_code = """ + selectId.value = NaN; + const eid = selectEvent.value.toString(); + const eventIds = src.data.eventId; + const mask = eventIds.map((evid) => evid === eid); + view.filters[1].booleans = mask; + src.change.emit(); + view.change.emit(); + """ + showAllButtonSim.js_on_click(CustomJS( + args=dict(view=viewId['Sim'], src=src['Sim'], selectEvent=numInput, selectId=clIdInputSim), + code=showAll_code + )) + showAllButtonReco.js_on_click(CustomJS( + args=dict(view=viewId['Reco'], src=src['Reco'], selectEvent=numInput, selectId=clIdInputReco), + code=showAll_code + )) + + menuVar_tuple = (('Energy [GeV]', 'energies_sum'),) + if fracs_in_df: + menuVar_tuple += (('Fraction', 'fracs_sum'),) + menuVar = [*menuVar_tuple] + dropVar = Dropdown(label="Z axis", button_type="warning", menu=menuVar, width=100) + + slider_calb = CustomJS( + args=dict( + srcSim=src["Sim"], srcReco=src["Reco"], + viewSim=view["Sim"], viewReco=view["Reco"], + threshSim=threshFilter["Sim"], threshReco=threshFilter["Reco"], + slider=slider, select=numInput, + varNameHolder=varNameHolder, + ), + code=""" + const minVal = slider.value; + const eid = select.value.toString(); + const v = varNameHolder.data['value'][0]; + + const sim = srcSim.data; + const rec = srcReco.data; + + let maskSim = []; + let maskRec = []; + + for (let i = 0; i < sim[v].length; i++) { + maskSim.push(sim[v][i] >= minVal && sim["eventId"][i] === eid); + } + for (let i = 0; i < rec[v].length; i++) { + maskRec.push(rec[v][i] >= minVal && rec["eventId"][i] === eid); + } + + threshSim.booleans = maskSim; + threshReco.booleans = maskRec; + + srcSim.change.emit(); + srcReco.change.emit(); + """ + ) + slider.js_on_change("value", slider_calb) + + mapperSim = {'energies': mapper_log['Sim'],'energies_sum': mapper_log['Sim']} + mapperReco = {'energies': mapper_log['Reco'],'energies_sum': mapper_log['Reco']} + maxVals = {'energies': 1.5, 'energies_sum': enSumMax} + if fracs_in_df: + mapperSim.update({'fracs': mapper_lin['Sim'],'fracs_sum': mapper_lin['Sim']}) + mapperReco.update({'fracs': mapper_lin['Reco'],'fracs_sum': mapper_lin['Reco']}) + maxVals.update({'fracs': 1.,'fracs_sum': 1.}) + + dropVar_calb = CustomJS( + args=dict( + srcSim=src['Sim'], srcReco=src['Reco'], + patchSim=p['Sim'][0].renderers[0], patchReco=p['Reco'][0].renderers[0], + mapperSim=mapperSim, mapperReco=mapperReco, + maxVals=maxVals, + colorBarSim=color_bar['Sim'], colorBarReco=color_bar['Reco'], + slider=slider, + slider_callback=slider_calb, + varNameHolder=varNameHolder, + ), + code=""" + const varName = this.item; + // Update color mapper range + colorBarSim.color_mapper = mapperSim[varName]; + colorBarSim.color_mapper.low = Math.min(...srcSim.data[varName]); + colorBarSim.color_mapper.high = Math.max(...srcSim.data[varName]); + colorBarReco.color_mapper = mapperReco[varName]; + colorBarReco.color_mapper.low = Math.min(...srcReco.data[varName]); + colorBarReco.color_mapper.high = Math.max(...srcReco.data[varName]); + // Update patch fill_color field + patchSim.glyph.fill_color.field = varName; + patchReco.glyph.fill_color.field = varName; + // Update slider range and value + slider.start = 0.; + slider.end = maxVals[varName]; + slider.value = slider.start; + slider.step = (slider.end - slider.start) / 500.; + slider.title = "Min threshold for " + varName; + // Update the varName in the slider callback + varNameHolder.data['value'][0] = varName; + // Update the sources + srcSim.change.emit(); + srcReco.change.emit(); + varNameHolder.change.emit(); + """ + ) + dropVar.js_on_event("menu_item_click", dropVar_calb) + + lay = [[writeIntructions()], + [numInput, Div(text='', width=40, height=1), slider], + [dropVar,], + [p['Sim'][0], p['Reco'][0]]] + + if clids_in_df: + lay.append([Div(text='', width=30, height=1), clIdInputSim, showAllButtonSim, + Div(text='', width=650, height=1), clIdInputReco, showAllButtonReco]) + lay.append([p['Sim'][1], p['Reco'][1]]) + + lay.append([writeContacts()]) + + save(layout(lay)) + print(f"INFO: Event plot saved to {output_path}") + +def showECAL(infile, outdir, props, outname='EventDisplay'): + utils.createDir(outdir) + parentDir = os.path.dirname(outdir) + if outdir[-1] == '/': + parentDir = os.path.dirname(parentDir) + utils.createIndexPHP(src=parentDir, dest=outdir) + + varsGeom = [ + "crystalDetId", + "crystalCenterEta", + "crystalCenterPhi", + "crystalCorner0Eta", + "crystalCorner1Eta", + "crystalCorner2Eta", + "crystalCorner3Eta", + "crystalCorner0Phi", + "crystalCorner1Phi", + "crystalCorner2Phi", + "crystalCorner3Phi", + ] + varsEventCommon = ["eventId"] + varsEvent = {"Reco": [], "Sim": []} + for pfix in ("Reco", "Sim"): + varsEvent[pfix].extend( + [x + pfix for x in ( + "energies", + "detids", + "nHits", + "clusterEnergies", + "clusterEtas", + "clusterPhis", + "clusterHitEnergies", + "clusterHitFractions", + "clusterHitClids", + "clusterHitDetids", + )] + ) + varsEventAll = varsEventCommon + varsEvent["Reco"] + varsEvent["Sim"] + + with uproot.open(infile) as file: + dfGeom = file["ecalGeometryAnalyzer/Geometry"].arrays(varsGeom, library="pandas") + if not props.geom: + dfEvent = file["ecalGeometryAnalyzer/Event"].arrays(varsEventAll, entry_stop=props.nevents, library="awkward") + + if props.geom: + plotGeom(dfGeom, output_path=os.path.join(outdir, "geom.html")) + return + + dfHits, dfClusters, dfHitsInClusters = ({} for _ in range(3)) + for pfix in ("Reco", "Sim"): + dfHits[pfix] = ak.to_dataframe( + dfEvent[["eventId", "energies"+pfix, "detids"+pfix]] + ).rename(columns={'energies'+pfix: 'energies', 'detids'+pfix: 'detids'}) + dfClusters[pfix] = ak.to_dataframe(dfEvent[["eventId", "clusterEnergies"+pfix, "clusterEtas"+pfix, "clusterPhis"+pfix]]) + dfHitsInClusters[pfix] = ak.to_dataframe( + dfEvent[["eventId", "clusterHitEnergies"+pfix, "clusterHitDetids"+pfix, "clusterHitFractions"+pfix, "clusterHitClids"+pfix]] + ).rename(columns={"clusterHitEnergies"+pfix: 'energies', "clusterHitDetids"+pfix: 'detids', + "clusterHitFractions"+pfix: 'fracs', "clusterHitClids"+pfix: 'clids'}) + + plotEvent( + dfGeom, + dfHitsInClusters, + dfClusters, + output_path=os.path.join(outdir, outname + "_clusterHits.html"), + ) + + if props.allhits: + plotEvent( + dfGeom, + dfHits, + dfClusters, + output_path=os.path.join(outdir, outname + "_allHits.html"), + ) + + print("INFO: Done.") + +@dataclass +class InputArgs: + nevents: int + geom: bool = False + clusters: bool = False + allhits: bool = False + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Show position of crystals.") + parser.add_argument("-i", "--file", help="Path to the input ROOT file.") + parser.add_argument("-o", "--outdir", help="Path to the output folder where the script outputs will be stored.") + parser.add_argument("--outname", default='EventDisplay', help="Name of the output html file with the event display.") + parser.add_argument("-n", "--nevents", help="Number of events to plot.", default=10, type=int) + parser.add_argument("-c", "--clusters", help="Add cluster information.", default=False, action='store_true') + geom_help_str = "Plot only the geometry. It highlights the position of the center and corners of each ECAL crystal." + parser.add_argument("-g", "--geom", help=geom_help_str, default=False, action='store_true') + all_hits_str = "On top of the clustered hits, add an identical visualization with all hits." + parser.add_argument("-a", "--allhits", help=all_hits_str, default=False, action='store_true') + + args = parser.parse_args() + props = InputArgs(nevents=args.nevents, geom=args.geom, clusters=args.clusters, allhits=args.allhits) + showECAL(args.file, args.outdir, props, args.outname) diff --git a/Validation/RecoParticleFlow/scripts/utils.py b/Validation/RecoParticleFlow/scripts/utils.py new file mode 100644 index 0000000000000..e15c2b3227a78 --- /dev/null +++ b/Validation/RecoParticleFlow/scripts/utils.py @@ -0,0 +1,92 @@ +import os +import numpy as np +import ROOT + +def angleDiff(x1, x2): + return np.arctan2(np.sin(x1 - x2), np.cos(x1 - x2)) + +def checkRootDir(afile, adir): + if not afile.Get(adir): + raise RuntimeError(f"Directory '{adir}' not found in {afile}") + +def createDir(adir): + if not os.path.exists(adir): + os.makedirs(adir) + return adir + +def createIndexPHP(src, dest): + """ + Copy index php file used for visualization in the browser. + """ + php_file = os.path.join(src, 'index.php') + if os.path.exists(php_file) and not os.path.exists(os.path.join(src, 'index.php')): + os.system(f'cp {php_file} {dest}') + +def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1): + """ + The meanForRange and rmsForRange are initial estimates. + """ + Xmax = histo.GetXaxis().GetBinCenter(histo.GetMaximumBin()) + + gausTF1 = ROOT.TF1() + + Pvalue = 0. + RangeLow = histo.GetBinLowEdge(2) + RangeUp = histo.GetBinLowEdge(histo.GetNbinsX()) + + PvalueBest = 0. + rms_step_minus = 2.2 + RangeLowBest = meanForRange - rms_step_minus*rmsForRange + RangeUpBest = meanForRange + rms_step_minus*rmsForRange + + range_max_dist = 0.7 + sigma_step = 0.1 + StepMinusBest, StepPlusBest, ChiSquareBest, ndfBest = (None for _ in range(4)) + + while rms_step_minus>range_max_dist: + RangeLow = meanForRange - rms_step_minus*rmsForRange + rms_step_plus = rms_step_minus + + while rms_step_plus>range_max_dist: + RangeUp = meanForRange + rms_step_plus*rmsForRange + histo.Fit("gaus", "0Q" if quiet else "0", "0", RangeLow, RangeUp) + + gausTF1 = histo.GetListOfFunctions().FindObject("gaus") + ChiSquare = gausTF1.GetChisquare() + ndf = gausTF1.GetNDF() + Pvalue = ROOT.TMath.Prob(ChiSquare, ndf) + + if Pvalue > PvalueBest: + PvalueBest = Pvalue + RangeLowBest = RangeLow + RangeUpBest = RangeUp + ndfBest = ndf + ChiSquareBest = ChiSquare + StepMinusBest = rms_step_minus + StepPlusBest = rms_step_plus + meanForRange = gausTF1.GetParameter(1) + + if not quiet: + print(f"\nFitting range used: [{meanForRange} - {rms_step_minus} sigma, {meanForRange} + {rms_step_plus} sigma ] ") + print(f"ChiSquare = {ChiSquare}, NDF = {ndf}, Prob = {Pvalue}, Best Prob so far = {PvalueBest}") + print(f"Sigma limit = {range_max_dist}") + + rms_step_plus -= sigma_step + + rms_step_minus -= sigma_step + + if quiet: + histo.Fit("gaus", "0Q", "0", RangeLowBest, RangeUpBest) + else: + histo.Fit("gaus","0","0", RangeLowBest, RangeUpBest) + print("Fit found!") + print(f"Final fitting range used: [{meanForRange} - {StepMinusBest} rms(WHF), {meanForRange} + {StepPlusBest} rms(WHF) ]") + print(f"ChiSquare = {ChiSquareBest}, NDF = {ndfBest}, Prob = {PvalueBest}\n\n") + + return histo.GetListOfFunctions().FindObject("gaus") + +def getParentDir(adir): + pardir = os.path.dirname(adir) + if adir[-1] == '/': + pardir = os.path.dirname(pardir) + return pardir diff --git a/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py b/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py new file mode 100644 index 0000000000000..6429f385c95d9 --- /dev/null +++ b/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py @@ -0,0 +1,155 @@ +import FWCore.ParameterSet.Config as cms +import FWCore.ParameterSet.VarParsing as VarParsing + +# cmsRun /ecalGeometryAnalyzer_cfg.py input=step2.root maxEvents=10 +opt = VarParsing.VarParsing('analysis') +opt.register( + 'input', '', + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.string, + "Input file (only one supported)" +) +opt.register( + 'output', '', + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.string, + "Output file." +) +opt.register( + 'kinematicCuts', False, + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.bool, + "Whether to apply similar kinematic cuts as done in the PF cluster validation." +) +opt.register( + 'enFracCut', 0.01, + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.float, + """ + Cut on the energy fraction (wrt CaloParticle energy) for each sim cluster. + Considered only if kinematicCuts = True. + """ +) +opt.register( + 'ptCut', 0.1, + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.float, + """ + Cut on the pT of each sim cluster. + Considered only if kinematicCuts = True. + """ +) +opt.register( + 'scoreCut', 1., + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.float, + """ + Cut on the score of the matching between a sim cluster and all reco clusters. + The score is a distance metric: 0 corresponds to a "perfect" matching, while 1 is the absence of matching. + Considered only if kinematicCuts = True. + """ +) +opt.register( + 'responseCut', 0., + VarParsing.VarParsing.multiplicity.singleton, + VarParsing.VarParsing.varType.float, + """ + Cut on the response of each sim cluster wrt to each reco cluster. + The event is filled only if there is at least one sim/reco combination with a response larger than the cut threshold. + Always considered. + """ +) +opt.parseArguments() + +def noDots(sss): + return str(sss).replace('.','p') + +if opt.output == '': + if opt.kinematicCuts: + opt.output = f'data_response{noDots(opt.responseCut)}_pt{noDots(opt.ptCut)}_enfrac{noDots(opt.enFracCut)}_score{noDots(opt.scoreCut)}.root' + else: + opt.output = f'data_response{noDots(opt.responseCut)}_nokincut.root' + +from Configuration.Eras.Era_Phase2C17I13M9_cff import Phase2C17I13M9 +from Configuration.ProcessModifiers.enableCPfromPU_cff import enableCPfromPU +process = cms.Process("EcalGeometryAnalyzer",Phase2C17I13M9,enableCPfromPU) + +process.load('Configuration.StandardSequences.Services_cff') +process.load('SimGeneral.HepPDTESSource.pythiapdt_cfi') +process.load('FWCore.MessageService.MessageLogger_cfi') +process.load('Configuration.EventContent.EventContent_cff') +process.load('SimGeneral.MixingModule.mixNoPU_cfi') +process.load('Configuration.Geometry.GeometryExtendedRun4D121Reco_cff') +process.load('Configuration.StandardSequences.MagneticField_cff') +process.load('Configuration.StandardSequences.SimL1Emulator_cff') +process.load('Configuration.StandardSequences.L1TrackTrigger_cff') +process.load('Configuration.StandardSequences.SimPhase2L1GlobalTriggerEmulator_cff') +process.load('L1Trigger.Configuration.Phase2GTMenus.SeedDefinitions.step1_2024.l1tGTMenu_cff') +process.load('Configuration.StandardSequences.Validation_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') + +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:phase2_realistic_T33', '') + +process.TFileService = cms.Service( + "TFileService", + fileName = cms.string(opt.output), + closeFileFast = cms.untracked.bool(True) +) + +process.load("FWCore.MessageService.MessageLogger_cfi") +process.MessageLogger.cerr.FwkReport.reportEvery = 1 +# process.MessageLogger.cerr.threshold = 'INFO' +# process.MessageLogger.cerr.INFO.limit = -1 +# process.MessageLogger.debugModules = ["*"] + +process.maxEvents = cms.untracked.PSet( + input = cms.untracked.int32(opt.maxEvents) +) + +process.source = cms.Source("PoolSource", + fileNames = cms.untracked.vstring('file:' + opt.input) +) + +ecalRecoClusters = "hltParticleFlowClusterECALUnseeded" + +process.hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", + hardScatterOnly = cms.bool(True), + hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), + hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), + # hltParticleFlowClusterHO +) + +process.hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", + associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), + label_lcl = cms.InputTag(ecalRecoClusters), + label_scl = cms.InputTag("mix","MergedCaloTruth") +) + +process.ecalGeometryAnalyzer = cms.EDAnalyzer( + 'EcalGeometryAnalyzer', + caloParticles = cms.InputTag("mix", "MergedCaloTruth"), + recHits = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), + simHits = cms.InputTag("g4SimHits", "EcalHitsEB"), + recClusters = cms.InputTag(ecalRecoClusters), + simClusters = cms.InputTag("mix", "MergedCaloTruth"), + kinematicCuts = cms.untracked.bool(opt.kinematicCuts), + enFracCut = cms.untracked.double(opt.enFracCut), + ptCut = cms.untracked.double(opt.ptCut), + scoreCut = cms.untracked.double(opt.scoreCut), + responseCut = cms.untracked.double(opt.responseCut), +) + +""" +This cut avoids the need to have associator information in the event +if it is not needed +""" +if opt.kinematicCuts or opt.responseCut > 0.: + process.ecalGeometryAnalyzer.clusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL") + process.p = cms.Path( + process.hltPFScAssocByEnergyScoreProducer + * process.hltPFClusterSimClusterAssociationProducerECAL + * process.ecalGeometryAnalyzer + ) +else: + process.p = cms.Path(process.ecalGeometryAnalyzer) From 1fd888da36b903bfe094681c4f96e9f22444e976 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 29 Jan 2026 18:30:42 +0100 Subject: [PATCH 20/64] Support Run3 in event display. --- .../scripts/showECALcrystals.py | 2 +- .../scripts/showECALcrystals_interactive.py | 4 +- .../test/ecalGeometryAnalyzer_cfg.py | 193 +++++++++--------- 3 files changed, 98 insertions(+), 101 deletions(-) diff --git a/Validation/RecoParticleFlow/scripts/showECALcrystals.py b/Validation/RecoParticleFlow/scripts/showECALcrystals.py index 6416ad9f706ee..bc016e1cd71f4 100755 --- a/Validation/RecoParticleFlow/scripts/showECALcrystals.py +++ b/Validation/RecoParticleFlow/scripts/showECALcrystals.py @@ -202,7 +202,7 @@ class InputArgs: parser.add_argument('-i', '--file', help='Path to the input ROOT file.') parser.add_argument('-o', '--outdir', help='Path to the output folder where the events will be stored.') parser.add_argument('-z', '--zoom', help='Zoom over a hard-coded eta/phi region: (min_eta, max_eta, min_phi, max_phi)', nargs=4, type=float, default=None) - parser.add_argument('-n', '--nevents', help='Number of events to plot.', default=6) + parser.add_argument('-n', '--nevents', help='Number of events to plot. If the input file has less events, then it plots all events of the input file.', default=10) args = parser.parse_args() props = InputArgs(zoom=args.zoom, nevents=args.nevents) diff --git a/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py b/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py index 8801c7bb6e683..1440e4dc83432 100755 --- a/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py +++ b/Validation/RecoParticleFlow/scripts/showECALcrystals_interactive.py @@ -98,7 +98,7 @@ def writeContacts(): Tool developed under the Next Generation Trigger project (task 3.1.1).

- Contact: For bug reports or feature requests please reach out to bruno.alves@cern.ch. + Contact: For bug reports or feature requests please write a message to bruno.alves@cern.ch.


""" @@ -697,7 +697,7 @@ class InputArgs: parser.add_argument("-i", "--file", help="Path to the input ROOT file.") parser.add_argument("-o", "--outdir", help="Path to the output folder where the script outputs will be stored.") parser.add_argument("--outname", default='EventDisplay', help="Name of the output html file with the event display.") - parser.add_argument("-n", "--nevents", help="Number of events to plot.", default=10, type=int) + parser.add_argument("-n", "--nevents", help="Number of events to plot. If the input file has less events, then it plots all events of the input file.", default=10, type=int) parser.add_argument("-c", "--clusters", help="Add cluster information.", default=False, action='store_true') geom_help_str = "Plot only the geometry. It highlights the position of the center and corners of each ECAL crystal." parser.add_argument("-g", "--geom", help=geom_help_str, default=False, action='store_true') diff --git a/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py b/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py index 6429f385c95d9..9b978ddc6669c 100644 --- a/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py +++ b/Validation/RecoParticleFlow/test/ecalGeometryAnalyzer_cfg.py @@ -1,128 +1,123 @@ +import argparse import FWCore.ParameterSet.Config as cms -import FWCore.ParameterSet.VarParsing as VarParsing - -# cmsRun /ecalGeometryAnalyzer_cfg.py input=step2.root maxEvents=10 -opt = VarParsing.VarParsing('analysis') -opt.register( - 'input', '', - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.string, - "Input file (only one supported)" -) -opt.register( - 'output', '', - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.string, - "Output file." -) -opt.register( - 'kinematicCuts', False, - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.bool, - "Whether to apply similar kinematic cuts as done in the PF cluster validation." -) -opt.register( - 'enFracCut', 0.01, - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.float, - """ - Cut on the energy fraction (wrt CaloParticle energy) for each sim cluster. - Considered only if kinematicCuts = True. - """ -) -opt.register( - 'ptCut', 0.1, - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.float, - """ - Cut on the pT of each sim cluster. - Considered only if kinematicCuts = True. - """ -) -opt.register( - 'scoreCut', 1., - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.float, - """ - Cut on the score of the matching between a sim cluster and all reco clusters. - The score is a distance metric: 0 corresponds to a "perfect" matching, while 1 is the absence of matching. - Considered only if kinematicCuts = True. - """ -) -opt.register( - 'responseCut', 0., - VarParsing.VarParsing.multiplicity.singleton, - VarParsing.VarParsing.varType.float, - """ - Cut on the response of each sim cluster wrt to each reco cluster. - The event is filled only if there is at least one sim/reco combination with a response larger than the cut threshold. - Always considered. - """ -) -opt.parseArguments() -def noDots(sss): - return str(sss).replace('.','p') +def noDots(obj): + return str(obj).replace('.','p') + +class dotDict(dict): + __getattr__ = dict.__getitem__ + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ -if opt.output == '': - if opt.kinematicCuts: - opt.output = f'data_response{noDots(opt.responseCut)}_pt{noDots(opt.ptCut)}_enfrac{noDots(opt.enFracCut)}_score{noDots(opt.scoreCut)}.root' +####################################################################################### +################ PARSER ############################################################### +####################################################################################### +parser = argparse.ArgumentParser(description='Produce ECAL geometry.') +eraChoices = ['Run3', 'Phase2'] + +parser.add_argument("-i", "--infile", help='Input file (only one supported).') +parser.add_argument("-o", "--outfile", default='', help='Output file.') +parser.add_argument("-k", "--kinematicCuts", type=bool, default=False, + help='Whether to apply similar kinematic cuts as done in the PF cluster validation.') +parser.add_argument("-e", "--enFracCut", type=float, default=0.01, + help='Cut on the energy fraction (wrt CaloParticle energy) for each sim cluster. Considered only if kinematicCuts = True.') +parser.add_argument("-p", "--ptCut", type=float, default=0.1, + help='Cut on the pT of each sim cluster. Considered only if kinematicCuts = True.') +parser.add_argument("-s", "--scoreCut", type=float, default=1., + help='Cut on the score of the matching between a sim cluster and all reco clusters. The score is a distance metric: 0 corresponds to a "perfect" matching, while 1 is the absence of matching. Considered only if kinematicCuts = True.') +parser.add_argument("-r", "--responseCut", type=float, default=0., + help='Cut on the response of each sim cluster with respect to each reco cluster. The event is filled only if there is at least one sim/reco combination with a response larger than the cut threshold. Always considered.') +parser.add_argument("--era", default='Phase2', choices=eraChoices, + help='Era (Run3 or Phase-2).') +parser.add_argument("-n", "--maxEvents", type=int, default=10, + help='Cut on the pT of each sim cluster. Considered only if kinematicCuts = True.') +args = parser.parse_args() + +if any(x in args.infile and x != args.era for x in eraChoices): + print('=========== WARNING ===================') + print('Please make sure the following input arguments make sense:') + print(f' --era {args.era}') + print(f' --infile {args.infile}') + print('======================================') + +if args.outfile == '': + if args.kinematicCuts: + args.outfile = f'data_response{noDots(args.responseCut)}_pt{noDots(args.ptCut)}_enfrac{noDots(args.enFracCut)}_score{noDots(args.scoreCut)}.root' else: - opt.output = f'data_response{noDots(opt.responseCut)}_nokincut.root' + args.outfile = f'data_response{noDots(args.responseCut)}_nokincut.root' -from Configuration.Eras.Era_Phase2C17I13M9_cff import Phase2C17I13M9 +cuts = dotDict(kin=args.kinematicCuts, enFrac=args.enFracCut, pt=args.ptCut, score=args.scoreCut, + response=args.responseCut) +####################################################################################### +####################################################################################### +####################################################################################### + +from Configuration.AlCa.GlobalTag import GlobalTag from Configuration.ProcessModifiers.enableCPfromPU_cff import enableCPfromPU -process = cms.Process("EcalGeometryAnalyzer",Phase2C17I13M9,enableCPfromPU) + +if args.era == 'Phase2': + from Configuration.Eras.Era_Phase2C22I13M9_cff import Phase2C22I13M9 + era = Phase2C22I13M9 + globalTag = 'auto:phase2_realistic_T33' +elif args.era == 'Run3': + from Configuration.Eras.Era_Run3_2025_cff import Run3_2025 + era = Run3_2025 + globalTag = 'auto:phase1_2025_realistic' + +process = cms.Process("EcalGeometryAnalyzer", era, enableCPfromPU) process.load('Configuration.StandardSequences.Services_cff') -process.load('SimGeneral.HepPDTESSource.pythiapdt_cfi') process.load('FWCore.MessageService.MessageLogger_cfi') -process.load('Configuration.EventContent.EventContent_cff') process.load('SimGeneral.MixingModule.mixNoPU_cfi') -process.load('Configuration.Geometry.GeometryExtendedRun4D121Reco_cff') process.load('Configuration.StandardSequences.MagneticField_cff') -process.load('Configuration.StandardSequences.SimL1Emulator_cff') -process.load('Configuration.StandardSequences.L1TrackTrigger_cff') -process.load('Configuration.StandardSequences.SimPhase2L1GlobalTriggerEmulator_cff') -process.load('L1Trigger.Configuration.Phase2GTMenus.SeedDefinitions.step1_2024.l1tGTMenu_cff') process.load('Configuration.StandardSequences.Validation_cff') process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') -from Configuration.AlCa.GlobalTag import GlobalTag -process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:phase2_realistic_T33', '') +if args.era == 'Phase2': + process.load('Configuration.Geometry.GeometryExtendedRun4D121Reco_cff') +elif args.era == 'Run3': + process.load('Configuration.Geometry.GeometryRecoDB_cff') + +process.GlobalTag = GlobalTag(process.GlobalTag, globalTag, '') process.TFileService = cms.Service( "TFileService", - fileName = cms.string(opt.output), + fileName = cms.string(args.outfile), closeFileFast = cms.untracked.bool(True) ) - + process.load("FWCore.MessageService.MessageLogger_cfi") process.MessageLogger.cerr.FwkReport.reportEvery = 1 # process.MessageLogger.cerr.threshold = 'INFO' # process.MessageLogger.cerr.INFO.limit = -1 # process.MessageLogger.debugModules = ["*"] - + process.maxEvents = cms.untracked.PSet( - input = cms.untracked.int32(opt.maxEvents) + input = cms.untracked.int32(args.maxEvents) ) +process.options.numberOfThreads=cms.untracked.uint32(1) +process.options.numberOfStreams=cms.untracked.uint32(1) process.source = cms.Source("PoolSource", - fileNames = cms.untracked.vstring('file:' + opt.input) + fileNames = cms.untracked.vstring('file:' + args.infile) ) -ecalRecoClusters = "hltParticleFlowClusterECALUnseeded" +from RecoLocalCalo.HGCalRecProducers.recHitMapProducer_cff import recHitMapProducer as _recHitMapProducer +process.hltRecHitMapProducer = _recHitMapProducer.clone( + hits = ["hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"], + doPFHits = True, + doHgcalHits = False, +) process.hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), - hitMapTag = cms.InputTag("hltRecHitMapProducer:barrelRecHitMap"), - hits = cms.VInputTag("hltParticleFlowRecHitECALUnseeded", "hltParticleFlowRecHitHBHE"), - # hltParticleFlowClusterHO + hitMapTag = cms.InputTag('hltRecHitMapProducer', 'pfRecHitMap'), + hits = cms.InputTag("hltRecHitMapProducer", "RefProdVectorPFRecHitCollection"), ) - + process.hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), - label_lcl = cms.InputTag(ecalRecoClusters), + label_lcl = cms.InputTag("hltParticleFlowClusterECALUnseeded"), label_scl = cms.InputTag("mix","MergedCaloTruth") ) @@ -131,25 +126,27 @@ def noDots(sss): caloParticles = cms.InputTag("mix", "MergedCaloTruth"), recHits = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), simHits = cms.InputTag("g4SimHits", "EcalHitsEB"), - recClusters = cms.InputTag(ecalRecoClusters), + recClusters = cms.InputTag("hltParticleFlowClusterECALUnseeded"), simClusters = cms.InputTag("mix", "MergedCaloTruth"), - kinematicCuts = cms.untracked.bool(opt.kinematicCuts), - enFracCut = cms.untracked.double(opt.enFracCut), - ptCut = cms.untracked.double(opt.ptCut), - scoreCut = cms.untracked.double(opt.scoreCut), - responseCut = cms.untracked.double(opt.responseCut), + kinematicCuts = cms.untracked.bool(cuts.kin), + enFracCut = cms.untracked.double(cuts.enFrac), + ptCut = cms.untracked.double(cuts.pt), + scoreCut = cms.untracked.double(cuts.score), + responseCut = cms.untracked.double(cuts.response), ) - + """ This cut avoids the need to have associator information in the event if it is not needed """ -if opt.kinematicCuts or opt.responseCut > 0.: +if cuts.kin or cuts.response > 0.: process.ecalGeometryAnalyzer.clusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL") process.p = cms.Path( - process.hltPFScAssocByEnergyScoreProducer + process.hltRecHitMapProducer + * process.hltPFScAssocByEnergyScoreProducer * process.hltPFClusterSimClusterAssociationProducerECAL * process.ecalGeometryAnalyzer ) else: process.p = cms.Path(process.ecalGeometryAnalyzer) + From ff1c144cb8899ed0500777794970107b825607e7 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 20 Feb 2026 11:56:39 +0100 Subject: [PATCH 21/64] Use utils, color blind palette and finer response eta binning Co-authored-by: Elena Vernazza --- .../RecoParticleFlow/plugins/PFTester.cc | 68 ++- .../scripts/makeHLTPFValidationPlots.py | 469 ++++++++++++------ 2 files changed, 368 insertions(+), 169 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 4d5a8852c9a78..e385fb98a4076 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -211,13 +211,23 @@ PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) } template -void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { +void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, + edm::Run const&, + edm::EventSetup const&) { std::string matching = doMatchByScore_ ? "MatchByScore" : "MatchByShEnF"; ibook.setCurrentFolder(outFolder_ + "/" + matching + "/CaloParticles"); h_CPToSCEnergyFraction_ = - ibook.book1D("CPToSCEnergyFraction", "CPToSCEnergyFraction;CaloParticle to SimCluster energy fraction", 100, 0, 2); + ibook.book1D("CaloParticleToSimClusterEnergyFraction", + "CaloParticleToSimClusterEnergyFraction;CaloParticle to SimCluster energy fraction", + 100, + 0, + 2); h_CPToSHEnergyFraction_ = - ibook.book1D("CPToSHEnergyFraction", "CPToSHEnergyFraction;CaloParticle to SimHits energy fraction", 100, 0, 2); + ibook.book1D("CaloParticleToSimHitsEnergyFraction", + "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", + 100, + 0, + 2); h_CP_recoToSimScore_ = ibook.book1D("CP_recoToSimScore", "CPrecoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); h_CP_simToRecoScore_ = @@ -443,7 +453,20 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, } } - ibook.setCurrentFolder(outFolder_ + "/" + matching + "/PFCandidates"); + for (auto& hVar : histoVarsSim) { + auto [nBins, hMin, hMax] = hVar.second; + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { + std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); + ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); + h2d_responsePt_[ithr][hVar.first] = + ibook.book2D("ResponsePt_" + hVar.first, "Response p_T;" + hVar.first, nBins, hMin, hMax, 50, 0., 1.5); + h2d_responseE_[ithr][hVar.first] = + ibook.book2D("ResponseE_" + hVar.first, "Response Energy;" + hVar.first, nBins, hMin, hMax, 50, 0., 1.5); + } + } + + ibook.setCurrentFolder(outFolder_ + "/PFCandidates"); + h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); @@ -481,11 +504,11 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, template void PFTesterT::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { - // -------------------------------------------------------------------- // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- - + // std::cout << std::endl; + // std::cout << "--- Analyze ---" << std::endl; edm::Handle Rechit; iEvent.getByToken(RechitToken_, Rechit); if (!Rechit.isValid()) { @@ -670,7 +693,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel auto const scTrack = simClusters[simId].g4Tracks()[0]; - const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); auto const simTrackEtaAtBoundary = pos.Eta(); if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; @@ -950,7 +973,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ----- Cluster response computation --------------------------------- // -------------------------------------------------------------------- - + // std::cout << std::endl; + // std::cout << "--- Event " << iEvent.eventAuxiliary().event() << " ---" << std::endl; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { double energySumSimHits = 0; for (auto hit_energy : simClusters[simId].hits_and_energies()) { @@ -967,7 +991,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel auto const scTrack = simClusters[simId].g4Tracks()[0]; - const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); auto const simTrackEtaAtBoundary = pos.Eta(); if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; @@ -992,18 +1016,34 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // fill only the best matched (lowest score) reco cluster, regardless split or merge for (const auto& recoPair : simToRecoMatchedSorted) { auto recoId = recoPair.first.index(); - + bool passMatch = false; if (doMatchByScore_) { // cut on score - passMatch = (recoPair.second.second < thresh); + passMatch = recoPair.second.second < thresh; } else { // cut on shared energy fraction double shared_energy = recoPair.second.first; double shared_energy_frac = shared_energy / energySumSimHits; - passMatch = (shared_energy_frac > thresh); + passMatch = shared_energy_frac > thresh; } + // std::cout << "===============================" << std::endl; + // std::cout << "matchByScore? " << doMatchByScore_ << std::endl; + // std::cout << "passMatch: " << passMatch << ", recoId: " << recoId << std::endl; + // std::cout << "sim en: " << energySumSimHits << ", reco en: " << recoClusters[recoId].energy() << std::endl; + // std::cout << "sim eta: " << simClusters[simId].eta() << ", reco eta: " << recoClusters[recoId].eta() << ", sim track eta: " << simTrackEtaAtBoundary << std::endl; + // std::cout << "sim phi: " << simClusters[simId].phi() << ", reco phi: " << recoClusters[recoId].phi() << std::endl; + // std::cout << "score: " << recoPair.second.second << std::endl; + // std::cout << "shared en frac: " << recoPair.second.first / energySumSimHits << std::endl; + // std::cout << "n sim clusters: " << simClusters.size() << std::endl; + // std::cout << "n matched reco clusters: " << simToRecoMatchedSorted.size() << std::endl; + // for (const auto& recoPairDebug : simToRecoMatchedSorted) { + // std::cout << "- score: " << recoPairDebug.second.second << ", share en frac: " << recoPairDebug.second.first / energySumSimHits << ", en: " << recoClusters[recoPairDebug.first.index()].energy() << std::endl; + // } + // std::cout << "threshold: " << thresh << std::endl; + // std::cout << "===============================" << std::endl; + if (passMatch) { // h2d_responsePt_[ithr]["En"]->Fill(energySumSimHits, // recoClusters[recoId].pt() / simClusters[simId].pt()); @@ -1031,6 +1071,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].energy() / energySumSimHits); + // std::cout << "fill response: " << recoClusters[recoId].energy() / energySumSimHits << std::endl; + // std::cout << "============== break =================" << std::endl; break; } } @@ -1162,7 +1204,7 @@ std::string PFTesterT::doubleToString(double x) const { result << std::setprecision(2) << x; std::string xnew = result.str(); - std::size_t pos = xnew.find("."); + std::size_t pos = xnew.find('.'); if (pos != std::string::npos) xnew.replace(pos, 1, "p"); else //if the double was provided without decimal places diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 9524eb66fb8df..269812b11d63c 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -5,15 +5,20 @@ import numpy as np import hist import re +import utils import matplotlib.pyplot as plt from matplotlib.transforms import blended_transform_factory from matplotlib.colors import LogNorm plt.style.use('tableau-colorblind10') +from dataclasses import dataclass import array import ROOT import mplhep as hep hep.style.use("CMS") +colorblind_palette = ('#1F77B4', '#AEC7E8', '#FF7F0E', '#FFBB78', '#2CA02C', '#98DF8A', '#D62728', + '#FF9896', '#9467BD', '#C5B0D5', '#8C564B', '#C49C94', '#E377C2', '#F7B6D2', + '#7F7F7F', '#C7C7C7', '#BCBD22', '#DBDB8D', '#17BECF', '#9EDAE5') import warnings warnings.filterwarnings("ignore", message="The value of the smallest subnormal") @@ -26,20 +31,6 @@ class dotdict(dict): def debug(mes): print('### INFO: ' + mes) -def createDir(adir): - if not os.path.exists(adir): - os.makedirs(adir) - return adir - -def createIndexPHP(src, dest): - php_file = os.path.join(src, 'index.php') - if os.path.exists(php_file): - os.system(f'cp {php_file} {dest}') - -def checkRootDir(afile, adir): - if not afile.Get(adir): - raise RuntimeError(f"Directory '{adir}' not found in {afile}") - def rate_errorbar_declutter(plotter, eff, err, yaxmin, frac=0.01): """ Filter uncertainties if they lie below the minimum (vertical) axis value. @@ -98,6 +89,23 @@ def histo_values_2D(h, error=False): ]) return values +@dataclass +class InputArgs: + xtitle: str + ytitle: str + rebin: tuple = None + ratio: str = '' + den: str = '' + num: str = '' + legden: str = '' + legnum: str = '' + var: str = '' + name: str = '' + unit: str = '' + fit: bool = False + logy: bool = False + normalize: bool = False + class Plotter: def __init__(self, label, fontsize=18, grid_color='grey'): self._fig, self._ax = plt.subplots(figsize=(10, 10)) @@ -120,7 +128,7 @@ def fig(self): def ax(self): return self._ax - def labels(self, x, y, legend_title=None, legend_loc='upper right'): + def labels(self, x, y, legend_title=None, legend_loc='best'): self._ax.set_xlabel(x) self._ax.set_ylabel(y) if legend_title is not None: @@ -172,11 +180,12 @@ def plotProject(h, props, rebin_edges, outname): """ Project and plot slices of a 2D histogram. """ - colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', - '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + colors_iter = iter(colorblind_palette) valuesList, errorsList = [], [] + fit_params = {} if props.fit else None plotter = Plotter(args.sample_label, grid_color=None) + for ibin, (low, high) in enumerate(zip(rebin_edges[:-1],rebin_edges[1:])): hproj = h.ProjectionY(h.GetName() + "_proj" + str(ibin), h.GetXaxis().FindBin(low), h.GetXaxis().FindBin(high), "e") @@ -189,24 +198,39 @@ def plotProject(h, props, rebin_edges, outname): line = plotter.ax.stairs(values, bin_edges, linewidth=2, baseline=None, color=next(colors_iter)) - label = f"{low} < {props['var']} < {high} {props['unit']}" + + label = f"{low} < {props.var} < {high} {props.unit}" plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, color=line.get_edgecolor(), fmt='s', label=label, **errorbar_kwargs) - plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) + if props.fit and hproj.GetMean() > 0. and hproj.GetRMS() > 0: + gausTF1 = utils.findBestGaussianCoreFit(hproj, meanForRange=0.9, rmsForRange=0.05, quiet=True) + nsigmas = 3 + xfunc = np.linspace(gausTF1.GetParameter(1) - nsigmas*abs(gausTF1.GetParameter(2)), + gausTF1.GetParameter(1) + nsigmas*abs(gausTF1.GetParameter(2))) + yfunc = np.array([gausTF1.Eval(xi) for xi in xfunc]) + if not (gausTF1.GetParameter(1) < hproj.GetBinLowEdge(1) + or gausTF1.GetParameter(1) > hproj.GetBinLowEdge(hproj.GetNbinsX())): + plotter.ax.plot(xfunc, yfunc, color=line.get_edgecolor(), + linewidth=1., linestyle='-') + fit_params[(low,high)] = (gausTF1.GetParameter(1), gausTF1.GetParameter(2), + gausTF1.GetParError(1), gausTF1.GetParError(2)) + + plotter.limits_with_margin(valuesList, errorsList, logY=props.logy) - plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') + plotter.labels(x=props.xtitle, y=props.ytitle, legend_title='') plotter.ax.grid(color='grey', axis='x') plt.tight_layout() plotter.save(outname) + return fit_params def plotOverlay(subdirs, cached_histos, name, props, outdir): """ Plots 1D distributions, overlaying plots with identical names in different 'subdirs'. """ - colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', - '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + colors_iter = iter(colorblind_palette) + pattern = r"Score(\d+)p(\d+)" matching = "score" if args.match_by_score else "shared energy fraction" replacement = lambda m: f"{matching} = {m.group(1)}.{m.group(2)}" @@ -215,17 +239,17 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): for sub in subdirs: root_hist = cached_histos[f"{sub}/{name}"] - if props['rebin'] is not None: + if props.rebin is not None: root_hist = root_hist.Clone(f"{name}" + "_clone") root_hist.SetDirectory(0) # detach from file - if isinstance(props['rebin'], (int, float)): - root_hist = root_hist.Rebin(int(props['rebin']), f"{name}" + "_rebin") - elif hasattr(props['rebin'], '__iter__'): - bin_edges_c = array.array('d', props['rebin']) + if isinstance(props.rebin, (int, float)): + root_hist = root_hist.Rebin(int(props.rebin), f"{name}" + "_rebin") + elif hasattr(props.rebin, '__iter__'): + bin_edges_c = array.array('d', props.rebin) root_hist = root_hist.Rebin(len(bin_edges_c) - 1, f"{name}" + "_rebin", bin_edges_c) else: - raise ValueError(f"Unknown type for rebin: {type(props['rebin'])}") + raise ValueError(f"Unknown type for rebin: {type(props.rebin)}") nbins, bin_edges, bin_centers, bin_widths = define_bins(root_hist) values, errors = histo_values_errors(root_hist) @@ -244,15 +268,15 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): fmt='s', label=sublabel, **errorbar_kwargs) plotter.limits(y=(0,1.1), x=(bin_edges[0], bin_edges[-1])) else: - if "Response" in name: - plotter.limits(y=(0, 2.0)) + # if "Response" in name: + # plotter.limits(y=(0, 2.0)) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) plotter.limits(x=(bin_edges[0], bin_edges[-1])) - plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') - plotter.ax.grid(color='grey') + plotter.labels(x=props.xtitle, y=props.ytitle, legend_title='') + plotter.ax.grid(color='grey') plt.tight_layout() plotter.save( os.path.join(outdir, name) ) @@ -260,8 +284,7 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): """ Plots 1D distributions of numerator / denominator. """ - colors_iter = iter(('#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', - '#984ea3', '#999999', '#e41a1c', '#dede00')) #colour-blind friendly + colors_iter = iter(colorblind_palette) pattern = r"Score(\d+)p(\d+)" matching = "score" if args.match_by_score else "shared energy fraction" replacement = lambda m: f"{matching} = {m.group(1)}.{m.group(2)}" @@ -271,21 +294,21 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): hist_num = cached_histos[f"{sub}/{num}"] hist_den = cached_histos[f"{sub}/{den}"] - if props['rebin'] is not None: + if props.rebin is not None: hist_num = hist_num.Clone(f"{sub}/{num}" + "_clone") hist_den = hist_den.Clone(f"{sub}/{den}" + "_clone") hist_num.SetDirectory(0) # detach from file hist_den.SetDirectory(0) # detach from file - if isinstance(props['rebin'], (int, float)): - hist_num = hist_num.Rebin(int(props['rebin']), f"{sub}/{num}" + "_rebin") - hist_den = hist_den.Rebin(int(props['rebin']), f"{sub}/{den}" + "_rebin") - elif hasattr(props['rebin'], '__iter__'): - bin_edges_c = array.array('d', props['rebin']) + if isinstance(props.rebin, (int, float)): + hist_num = hist_num.Rebin(int(props.rebin), f"{sub}/{num}" + "_rebin") + hist_den = hist_den.Rebin(int(props.rebin), f"{sub}/{den}" + "_rebin") + elif hasattr(props.rebin, '__iter__'): + bin_edges_c = array.array('d', props.rebin) hist_num = hist_num.Rebin(len(bin_edges_c) - 1, f"{sub}/{num}" + "_rebin", bin_edges_c) hist_den = hist_den.Rebin(len(bin_edges_c) - 1, f"{sub}/{den}" + "_rebin", bin_edges_c) else: - raise ValueError(f"Unknown type for rebin: {type(props['rebin'])}") + raise ValueError(f"Unknown type for rebin: {type(props.rebin)}") nbins, bin_edges, bin_centers, bin_widths = define_bins(hist_num) num_vals, num_errors = histo_values_errors(hist_num) @@ -306,12 +329,12 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): # plotter.limits_with_margin(valuesList, errorsList, logY=props['logy']) plotter.limits(x=(bin_edges[0], bin_edges[-1])) - if "Resolution" in props['name']: + if "Resolution" in props.name: plotter.limits(y=(0, 0.7)) - plotter.labels(x=props['xtitle'], y=props['ytitle'], legend_title='') + plotter.labels(x=props.xtitle, y=props.ytitle, legend_title='') plotter.ax.grid(color='grey') plt.tight_layout() - plotter.save( os.path.join(outdir, props['name']) ) + plotter.save( os.path.join(outdir, props.name) ) def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, suffix=''): """ @@ -324,13 +347,13 @@ def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, su valuesList, errorsList = [], [] colors_iter = iter(('black', 'blue')) - histo_names = [vars1d['den'], vars1d['num'], vars1d['ratio']] - leg_names = [vars1d['legden'], vars1d['legnum'], ''] - rebin = vars1d['rebin'] - doNormalize = vars1d['normalize'] - logy = vars1d['logy'] - xlabel = vars1d['xtitle'] - ylabel = vars1d['ytitle'] + histo_names = [vars1d.den, vars1d.num, vars1d.ratio] + leg_names = [vars1d.legden, vars1d.legnum, ''] + rebin = vars1d.rebin + doNormalize = vars1d.normalize + logy = vars1d.logy + xlabel = vars1d.xtitle + ylabel = vars1d.ytitle for name, leglabel in zip(histo_names, leg_names): @@ -479,6 +502,8 @@ def __call__(self, parser, namespace, values, option_string=None): parser.add_argument('-o', '--odir', default="HLTPFValidationPlots", help='Path to the output directory.') parser.add_argument('-l', '--sample_label', default="", help='Sample label for plotting.') parser.add_argument('-e', '--era', default="Phase2", help="Chose between ['Phase2', 'Run3'].") + parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') + parser.add_argument('--PtCut', default=0.1, help='Cut on the sim cluster energy fraction.') parser.add_argument('--match_by_score', default=1, type=int, help='Use association based on score (if false, use shared energy fraction).') parser.add_argument('--ticl', default=False, action='store_true', help='Use TiclBarrel folder.') @@ -491,11 +516,11 @@ def __call__(self, parser, namespace, values, option_string=None): args = parser.parse_args() - createDir(args.odir) + utils.createDir(args.odir) parentDir = os.path.dirname(args.odir) if args.odir[-1] == '/': parentDir = os.path.dirname(parentDir) - createIndexPHP(src=parentDir, dest=args.odir) + utils.createIndexPHP(src=parentDir, dest=args.odir) fontsize = 16 colors = hep.style.CMS['axes.prop_cycle'].by_key()['color'] @@ -525,7 +550,7 @@ def __call__(self, parser, namespace, values, option_string=None): else: sub_folder = 'ParticleFlow' dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/{matching}/PFClusterValidation" afile = ROOT.TFile.Open(args.file) - checkRootDir(afile, dqm_dir) + utils.checkRootDir(afile, dqm_dir) debug('Start caching PFCluster histograms...') subdirs = [] @@ -544,70 +569,100 @@ def __call__(self, parser, namespace, values, option_string=None): cached_histos[name] = key.ReadObj() debug(' ...done.') + # create and setup folders for subdir in subdirs: - checkRootDir(afile, f"{dqm_dir}/{subdir}") - createDir(f'{args.odir}/{subdir}') - createIndexPHP(src=args.odir, dest=f'{args.odir}/{subdir}') - + utils.checkRootDir(afile, f"{dqm_dir}/{subdir}") + utils.createDir(f'{args.odir}/{subdir}') + utils.createIndexPHP(src=args.odir, dest=f'{args.odir}/{subdir}') + + for subdir in subdirs: varsDict = { # Cluster efficiency - f'{subdir}/Eff_vs_En': dict(ratio=f'{subdir}/Eff_vs_En', + f'{subdir}/Eff_vs_En': InputArgs( + ratio=f'{subdir}/Eff_vs_En', den=f'SimClustersEn', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersEn', legnum='Matched SimClusters', - xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_EnFrac': dict(ratio=f'{subdir}/Eff_vs_EnFrac', + xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Eff_vs_EnFrac': InputArgs( + ratio=f'{subdir}/Eff_vs_EnFrac', den=f'SimClustersEnFrac', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersEnFrac', legnum='Matched SimClusters', - xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_EnSimTrack': dict(ratio=f'{subdir}/Eff_vs_EnSimTrack', + xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Eff_vs_EnSimTrack': InputArgs( + ratio=f'{subdir}/Eff_vs_EnSimTrack', den=f'SimClustersEnSimTrack', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersEnSimTrack', legnum='Matched SimClusters', - xtitle='SimTrack Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_Pt': dict(ratio=f'{subdir}/Eff_vs_Pt', + xtitle='SimTrack Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Eff_vs_Pt': InputArgs( + ratio=f'{subdir}/Eff_vs_Pt', den=f'SimClustersPt', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersPt', legnum='Matched SimClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Eff_vs_Eta': dict(ratio=f'{subdir}/Eff_vs_Eta', + xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Eff_vs_Eta': InputArgs( + ratio=f'{subdir}/Eff_vs_Eta', den=f'SimClustersEta', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersEta', legnum='Matched SimClusters', - xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Eff_vs_Phi': dict(ratio=f'{subdir}/Eff_vs_Phi', + xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False + ), + f'{subdir}/Eff_vs_Phi': InputArgs( + ratio=f'{subdir}/Eff_vs_Phi', den=f'SimClustersPhi', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersPhi', legnum='Matched SimClusters', - xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Eff_vs_Mult': dict(ratio=f'{subdir}/Eff_vs_Mult', + xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False + ), + f'{subdir}/Eff_vs_Mult': InputArgs( + ratio=f'{subdir}/Eff_vs_Mult', den=f'SimClustersMult', legden='SimClusters', num=f'{subdir}/SimClustersMatchedRecoClustersMult', legnum='Matched SimClusters', - xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), # Cluster split rate - f'{subdir}/Split_vs_En': dict(ratio=f'{subdir}/Split_vs_En', + f'{subdir}/Split_vs_En': InputArgs( + ratio=f'{subdir}/Split_vs_En', den=f'SimClustersEn', legden='SimClusters', - num=f'{subdir}/SimClustersMultiMatchedRecoClustersEn', legnum='Multi Matched SimClusters', - xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_EnFrac': dict(ratio=f'{subdir}/Split_vs_EnFrac', + num=f'{subdir}/SimClustersMultiMatchedRecoClustersEn', legnum='Multi Matched SimClusters', + xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Split_vs_EnFrac': InputArgs( + ratio=f'{subdir}/Split_vs_EnFrac', den=f'SimClustersEnFrac', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnFrac', legnum='Multi Matched SimClusters', - xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_EnSimTrack': dict(ratio=f'{subdir}/Split_vs_EnSimTrack', + xtitle='Energy Fraction', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Split_vs_EnSimTrack': InputArgs( + ratio=f'{subdir}/Split_vs_EnSimTrack', den=f'SimClustersEnSimTrack', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEnSimTrack', legnum='Multi Matched SimClusters', - xtitle='SimCluster Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_Pt': dict(ratio=f'{subdir}/Split_vs_Pt', + xtitle='SimTrack Energy [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Split_vs_Pt': InputArgs( + ratio=f'{subdir}/Split_vs_Pt', den=f'SimClustersPt', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersPt', legnum='Multi Matched SimClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Split_vs_Eta': dict(ratio=f'{subdir}/Split_vs_Eta', + xtitle=r'$p_{T}$ [GeV]', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), + f'{subdir}/Split_vs_Eta': InputArgs( + ratio=f'{subdir}/Split_vs_Eta', den=f'SimClustersEta', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersEta', legnum='Multi Matched SimClusters', - xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Split_vs_Phi': dict(ratio=f'{subdir}/Split_vs_Phi', + xtitle=r'$\eta$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False + ), + f'{subdir}/Split_vs_Phi': InputArgs( + ratio=f'{subdir}/Split_vs_Phi', den=f'SimClustersPhi', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersPhi', legnum='Multi Matched SimClusters', - xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Split_vs_Mult': dict(ratio=f'{subdir}/Split_vs_Mult', + xtitle=r'$\phi$', ytitle=nSimClustersLabel, rebin=None, logy=False, normalize=False + ), + f'{subdir}/Split_vs_Mult': InputArgs( + ratio=f'{subdir}/Split_vs_Mult', den=f'SimClustersMult', legden='SimClusters', num=f'{subdir}/SimClustersMultiMatchedRecoClustersMult', legnum='Multi Matched SimClusters', - xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False), + xtitle='Multiplicity', ytitle=nSimClustersLabel, rebin=4, logy=False, normalize=False + ), } # Compare pairs of variables @@ -616,47 +671,66 @@ def __call__(self, parser, namespace, values, option_string=None): varsDict = { # Cluster fake rate - f'{subdir}/Fake_vs_En': dict(ratio=f'{subdir}/Fake_vs_En', + f'{subdir}/Fake_vs_En': InputArgs( + ratio=f'{subdir}/Fake_vs_En', den=f'RecoClustersEn', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersEn', legnum='Matched RecoClusters', - xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Fake_vs_Pt': dict(ratio=f'{subdir}/Fake_vs_Pt', + xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4 + ), + f'{subdir}/Fake_vs_Pt': InputArgs( + ratio=f'{subdir}/Fake_vs_Pt', den=f'RecoClustersPt', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersPt', legnum='Matched RecoClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Fake_vs_Eta': dict(ratio=f'{subdir}/Fake_vs_Eta', + xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4 + ), + f'{subdir}/Fake_vs_Eta': InputArgs( + ratio=f'{subdir}/Fake_vs_Eta', den=f'RecoClustersEta', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersEta', legnum='Matched RecoClusters', - xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Fake_vs_Phi': dict(ratio=f'{subdir}/Fake_vs_Phi', + xtitle=r'$\eta$', ytitle=nPFClustersLabel + ), + f'{subdir}/Fake_vs_Phi': InputArgs( + ratio=f'{subdir}/Fake_vs_Phi', den=f'RecoClustersPhi', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersPhi', legnum='Matched RecoClusters', - xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Fake_vs_Mult': dict(ratio=f'{subdir}/Fake_vs_Mult', + xtitle=r'$\phi$', ytitle=nPFClustersLabel + ), + f'{subdir}/Fake_vs_Mult': InputArgs( + ratio=f'{subdir}/Fake_vs_Mult', den=f'RecoClustersMult', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', - xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4 + ), # Cluster merge rate (WIP) - f'{subdir}/Merge_vs_En': dict(ratio=f'{subdir}/Merge_vs_En', + f'{subdir}/Merge_vs_En': InputArgs( + ratio=f'{subdir}/Merge_vs_En', den=f'RecoClustersEn', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersEn', legnum='Multi Matched RecoClusters', - xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Merge_vs_Pt': dict(ratio=f'{subdir}/Merge_vs_Pt', + xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4), + f'{subdir}/Merge_vs_Pt': InputArgs( + ratio=f'{subdir}/Merge_vs_Pt', den=f'RecoClustersPt', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersPt', legnum='Multi Matched RecoClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), - f'{subdir}/Merge_vs_Eta': dict(ratio=f'{subdir}/Merge_vs_Eta', + xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4 + ), + f'{subdir}/Merge_vs_Eta': InputArgs( + ratio=f'{subdir}/Merge_vs_Eta', den=f'RecoClustersEta', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersEta', legnum='Multi Matched RecoClusters', - xtitle=r'$\eta$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Merge_vs_Phi': dict(ratio=f'{subdir}/Merge_vs_Phi', + xtitle=r'$\eta$', ytitle=nPFClustersLabel + ), + f'{subdir}/Merge_vs_Phi': InputArgs( + ratio=f'{subdir}/Merge_vs_Phi', den=f'RecoClustersPhi', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersPhi', legnum='Multi Matched RecoClusters', - xtitle=r'$\phi$', ytitle=nPFClustersLabel, rebin=None, logy=False, normalize=False), - f'{subdir}/Merge_vs_Mult': dict(ratio=f'{subdir}/Merge_vs_Mult', + xtitle=r'$\phi$', ytitle=nPFClustersLabel + ), + f'{subdir}/Merge_vs_Mult': InputArgs( + ratio=f'{subdir}/Merge_vs_Mult', den=f'RecoClustersMult', legden='RecoClusters', num=f'{subdir}/RecoClustersMultiMatchedSimClustersMult', legnum='Multi Matched RecoClusters', - xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, logy=False, normalize=False), + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4 + ), } # Compare pairs of variables @@ -664,68 +738,151 @@ def __call__(self, parser, namespace, values, option_string=None): plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') varsOverlay = { - "ResponseE_En_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='SimCluster Energy [GeV]', logy=False), - "ResponseE_EnFrac_Mean" : dict(ytitle=titles['response'], rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), xtitle='Energy Fraction', logy=False), - "ResponseE_EnSimTrack_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle='SimTrack Energy [GeV]', logy=False), - "ResponseE_Pt_Mean" : dict(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle=r'$p_{T} [GeV]$', logy=False), - "ResponseE_Eta_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\eta$', logy=False), - "ResponseE_Phi_Mean" : dict(ytitle=titles['response'], rebin=None, xtitle=r'$\phi$', logy=False), - "ResponseE_Mult_Mean" : dict(ytitle=titles['response'], rebin=4, xtitle='Multiplicity', logy=False), - "Eff_vs_En" : dict(ytitle=titles['eff'], rebin=4, xtitle='SimCluster Energy [GeV]', logy=False), - "Eff_vs_EnFrac" : dict(ytitle=titles['eff'], rebin=6, xtitle='Energy Fraction', logy=False), - "Eff_vs_EnSimTrack" : dict(ytitle=titles['eff'], rebin=4, xtitle='SimTrack Energy [GeV]', logy=False), - "Eff_vs_Pt" : dict(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Eff_vs_Eta" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\eta$', logy=False), - "Eff_vs_Phi" : dict(ytitle=titles['eff'], rebin=None, xtitle=r'$\phi$', logy=False), - "Eff_vs_Mult" : dict(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity', logy=False), - "Split_vs_En" : dict(ytitle=titles['split'], rebin=4, xtitle='SimCluster Energy [GeV]', logy=False), - "Split_vs_EnFrac" : dict(ytitle=titles['split'], rebin=6, xtitle='Energy Fraction', logy=False), - "Split_vs_EnSimTrack" : dict(ytitle=titles['split'], rebin=4, xtitle='SimTrack Energy [GeV]', logy=False), - "Split_vs_Pt" : dict(ytitle=titles['split'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Split_vs_Eta" : dict(ytitle=titles['split'], rebin=None, xtitle=r'$\eta$', logy=False), - "Split_vs_Phi" : dict(ytitle=titles['split'], rebin=None, xtitle=r'$\phi$', logy=False), - "Split_vs_Mult" : dict(ytitle=titles['split'], rebin=4, xtitle='Multiplicity', logy=False), - "Fake_vs_En" : dict(ytitle=titles['fake'], rebin=4, xtitle='Energy [GeV]', logy=False), - "Fake_vs_Pt" : dict(ytitle=titles['fake'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Fake_vs_Eta" : dict(ytitle=titles['fake'], rebin=None, xtitle=r'$\eta$', logy=False), - "Fake_vs_Phi" : dict(ytitle=titles['fake'], rebin=None, xtitle=r'$\phi$', logy=False), - "Fake_vs_Mult" : dict(ytitle=titles['fake'], rebin=4, xtitle='Multiplicity', logy=False), - "Merge_vs_En" : dict(ytitle=titles['merge'], rebin=4, xtitle='Energy [GeV]', logy=False), - "Merge_vs_Pt" : dict(ytitle=titles['merge'], rebin=4, xtitle='$p_{T} [GeV]$', logy=False), - "Merge_vs_Eta" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\eta$', logy=False), - "Merge_vs_Phi" : dict(ytitle=titles['merge'], rebin=None, xtitle=r'$\phi$', logy=False), - "Merge_vs_Mult" : dict(ytitle=titles['merge'], rebin=4, xtitle='Multiplicity', logy=False), + "ResponseE_En_Mean" : InputArgs(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), + xtitle='SimCluster Energy [GeV]'), + "ResponseE_EnFrac_Mean" : InputArgs(ytitle=titles['response'], rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), + xtitle='Energy Fraction'), + "ResponseE_EnSimTrack_Mean" : InputArgs(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), + xtitle='SimTrack Energy [GeV]'), + "ResponseE_Pt_Mean" : InputArgs(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), xtitle=r'$p_{T} [GeV]$'), + "ResponseE_Eta_Mean" : InputArgs(ytitle=titles['response'], xtitle=r'$\eta$'), + "ResponseE_Phi_Mean" : InputArgs(ytitle=titles['response'], xtitle=r'$\phi$'), + "ResponseE_Mult_Mean" : InputArgs(ytitle=titles['response'], rebin=4, xtitle='Multiplicity'), + "Eff_vs_En" : InputArgs(ytitle=titles['eff'], rebin=4, xtitle='SimCluster Energy [GeV]'), + "Eff_vs_EnFrac" : InputArgs(ytitle=titles['eff'], rebin=4, xtitle='Energy Fraction'), + "Eff_vs_EnSimTrack" : InputArgs(ytitle=titles['eff'], rebin=6, xtitle='SimTrack Energy'), + "Eff_vs_Pt" : InputArgs(ytitle=titles['eff'], rebin=4, xtitle='$p_{T} [GeV]$'), + "Eff_vs_Eta" : InputArgs(ytitle=titles['eff'], xtitle=r'$\eta$'), + "Eff_vs_Phi" : InputArgs(ytitle=titles['eff'], xtitle=r'$\phi$'), + "Eff_vs_Mult" : InputArgs(ytitle=titles['eff'], rebin=4, xtitle='Multiplicity'), + "Split_vs_En" : InputArgs(ytitle=titles['split'], rebin=4, xtitle='SimCluster Energy [GeV]'), + "Split_vs_EnFrac" : InputArgs(ytitle=titles['split'], rebin=6, xtitle='Energy Fraction'), + "Split_vs_EnSimTrack" : InputArgs(ytitle=titles['split'], rebin=4, xtitle='SimTrack Energy [GeV]'), + "Split_vs_Pt" : InputArgs(ytitle=titles['split'], rebin=4, xtitle='$p_{T} [GeV]$'), + "Split_vs_Eta" : InputArgs(ytitle=titles['split'], xtitle=r'$\eta$'), + "Split_vs_Phi" : InputArgs(ytitle=titles['split'], xtitle=r'$\phi$'), + "Split_vs_Mult" : InputArgs(ytitle=titles['split'], rebin=4, xtitle='Multiplicity'), + "Fake_vs_En" : InputArgs(ytitle=titles['fake'], rebin=4, xtitle='Energy [GeV]'), + "Fake_vs_Pt" : InputArgs(ytitle=titles['fake'], rebin=4, xtitle='$p_{T} [GeV]$'), + "Fake_vs_Eta" : InputArgs(ytitle=titles['fake'], xtitle=r'$\eta$'), + "Fake_vs_Phi" : InputArgs(ytitle=titles['fake'], xtitle=r'$\phi$'), + "Fake_vs_Mult" : InputArgs(ytitle=titles['fake'], rebin=4, xtitle='Multiplicity'), + "Merge_vs_En" : InputArgs(ytitle=titles['merge'], rebin=4, xtitle='Energy [GeV]'), + "Merge_vs_Pt" : InputArgs(ytitle=titles['merge'], rebin=4, xtitle='$p_{T} [GeV]$'), + "Merge_vs_Eta" : InputArgs(ytitle=titles['merge'], xtitle=r'$\eta$'), + "Merge_vs_Phi" : InputArgs(ytitle=titles['merge'], xtitle=r'$\phi$'), + "Merge_vs_Mult" : InputArgs(ytitle=titles['merge'], rebin=4, xtitle='Multiplicity'), } for name, props in varsOverlay.items(): plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) varsResponse = { - ("ResponseE_En_Sigma", "ResponseE_En_Mean") : dict(name='ResolutionEn', ytitle=titles['resolution'], xtitle='SimCluster Energy [GeV]', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), - ("ResponseE_EnFrac_Sigma", "ResponseE_EnFrac_Mean") : dict(name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle='Energy Fraction', rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.), logy=False), - ("ResponseE_EnSimTrack_Sigma", "ResponseE_EnSimTrack_Mean") : dict(name='ResolutionEnSimTrack', ytitle=titles['resolution'], xtitle='SimTrack Energy [GeV]', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), - ("ResponseE_Pt_Sigma", "ResponseE_Pt_Mean") : dict(name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', rebin=(0., 5., 10., 20., 40., 60., 100.), logy=False), - ("ResponseE_Eta_Sigma", "ResponseE_Eta_Mean") : dict(name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$', rebin=None, logy=False), - ("ResponseE_Phi_Sigma", "ResponseE_Phi_Mean") : dict(name='ResolutionPhi',ytitle=titles['resolution'], xtitle=r'$\phi$', rebin=None, logy=False), - ("ResponseE_Mult_Sigma", "ResponseE_Mult_Mean") : dict(name='ResolutionMult',ytitle=titles['resolution'], xtitle='Multiplicity', rebin=4, logy=False), + ("ResponseE_En_Sigma", "ResponseE_En_Mean"): + InputArgs( + name='ResolutionEn', ytitle=titles['resolution'], xtitle=r'SimCluster Energy [GeV]', + rebin=(0., 5., 10., 20., 40., 60., 100.) + ), + ("ResponseE_EnFrac_Sigma", "ResponseE_EnFrac_Mean"): + InputArgs( + name='ResolutionEnFrac', ytitle=titles['resolution'], xtitle=r'Energy Fraction', + rebin=(0., 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.) + ), + ("ResponseE_EnSimTrack_Sigma", "ResponseE_EnSimTrack_Mean"): + InputArgs( + name='ResolutionEnSimTrack', ytitle=titles['resolution'], xtitle=r'SimTrack Energy [GeV]', + rebin=(0., 5., 10., 20., 40., 60., 100.) + ), + ("ResponseE_Pt_Sigma", "ResponseE_Pt_Mean"): + InputArgs( + name='ResolutionPt', ytitle=titles['resolution'], xtitle=r'$p_{T} [GeV]$', + rebin=(0., 5., 10., 20., 40., 60., 100.) + ), + ("ResponseE_Eta_Sigma", "ResponseE_Eta_Mean"): + InputArgs( + name='ResolutionEta', ytitle=titles['resolution'], xtitle=r'$\eta$' + ), + ("ResponseE_Phi_Sigma", "ResponseE_Phi_Mean"): + InputArgs( + name='ResolutionPhi', ytitle=titles['resolution'], xtitle=r'$\phi$' + ), + ("ResponseE_Mult_Sigma", "ResponseE_Mult_Mean"): + InputArgs( + name='ResolutionMult', ytitle=titles['resolution'], xtitle='Multiplicity', rebin=4 + ), } for (num, den), props in varsResponse.items(): plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir=args.odir) vars2DProjection = { - **{f'{subdir}/ResponseE_En': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'E', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_EnFrac': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'Energy Fraction', unit='', logy=False, rebin=(0., 0.1, 0.5, 0.9, 1.)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_EnSimTrack': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$E_{SimTrack}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_Pt': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', logy=False, rebin=(0., 1., 3., 10., 100.)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_Eta': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', unit='', logy=False, rebin=(-1.5, -0.75, 0., 0.75, 1.5)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_Phi': dict(xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', unit='', logy=False, rebin=(-3.15, -1.5, 0., 1.5, 3.15)) for subdir in subdirs}, - **{f'{subdir}/ResponseE_Mult': dict(xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', unit='', logy=False, rebin=(0., 20., 50., 100., 200.)) for subdir in subdirs}, - 'SimClustersEnFrac_Mult': dict(xtitle='Multiplicity', ytitle='# Clusters', var='Energy Fraction', unit='', logy=True, rebin=(0., 0.005, 0.01, 0.02, 0.03, 1.)), + **{f'{subdir}/ResponseE_En': + InputArgs( + fit=True, xtitle=titles['response'], ytitle='# Clusters', var=r'E', unit='[GeV]', + rebin=(0., 20., 40., 60, 80., 100.) + ) for subdir in subdirs}, + **{f'{subdir}/ResponseE_EnFrac': + InputArgs( + xtitle=titles['response'], ytitle='# Clusters', var=r'Energy Fraction', + rebin=(0., 0.1, 0.5, 0.9, 1.) + ) for subdir in subdirs}, + **{f'{subdir}/ResponseE_EnSimTrack': + InputArgs( + xtitle=titles['response'], ytitle='# Clusters', var=r'$E_{SimTrack}$', unit='[GeV]', + rebin=(0., 20., 40., 60, 80., 100.) + ) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Pt': + InputArgs( + xtitle=titles['response'], ytitle='# Clusters', var=r'$p_{T}$', unit='[GeV]', + rebin=(0., 20., 40., 60, 80., 100.) + ) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Eta': + InputArgs( + xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', + rebin=(-1.5, -1.3, -1., -0.75, -0.5, -0.25, 0., 0.25, 0.5, 0.75, 1., 1.3, 1.5) + ) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Phi': + InputArgs( + xtitle=titles['response'], ytitle='# Clusters', var=r'$\phi$', + rebin=(-3.15, -1.5, 0., 1.5, 3.15) + ) for subdir in subdirs}, + **{f'{subdir}/ResponseE_Mult': + InputArgs( + xtitle=titles['response'], ytitle='# Clusters', var='Multiplicity', + rebin=(0, 50, 100, 150, 200) + ) for subdir in subdirs}, + 'SimClustersEnFrac_Mult': + InputArgs( + xtitle='Multiplicity', ytitle='# Clusters', var='Energy Fraction', + logy=True, rebin=(0., 0.005, 0.01, 0.02, 0.03, 1.) + ), } for name, props in vars2DProjection.items(): - plotter = Plotter(args.sample_label, fontsize=15) root_hist = cached_histos[f"{name}"] - plotProject(root_hist, props, rebin_edges=props['rebin'], outname=os.path.join(args.odir, name + '_Projected')) + fitpars = plotProject(root_hist, props, rebin_edges=props.rebin, outname=os.path.join(args.odir, name + '_Projected')) + + if props.fit: + n_bins = len(fitpars) + xmin = min(low for low, _ in fitpars.keys()) + xmax = max(high for _, high in fitpars.keys()) + + fitstr = 'FromFit{}_'+os.path.basename(name) + fitdir = os.path.dirname(name) + '/' + fitstr + cached_histos[fitdir.format('Mean')] = ROOT.TH1F(fitdir.format('Mean').replace('/','_'), fitstr.format('Mean'), n_bins, xmin, xmax) + cached_histos[fitdir.format('Width')] = ROOT.TH1F(fitdir.format('Width').replace('/','_'), fitstr.format('Width'), n_bins, xmin, xmax) + + for i, ((low, high), (mean, width, mean_err, width_err)) in enumerate(fitpars.items(), 1): + cached_histos[fitdir.format('Mean')].SetBinContent(i, mean) + cached_histos[fitdir.format('Mean')].SetBinError(i, mean_err) + cached_histos[fitdir.format('Width')].SetBinContent(i, width) + cached_histos[fitdir.format('Width')].SetBinError(i, width_err) + + for name, props in vars2DProjection.items(): + if props.fit: + props.xtitle = 'Energy [GeV]' + props.ytitle = 'Response' + plotOverlay(subdirs, cached_histos, fitstr.format('Mean'), props, outdir=args.odir) + props.ytitle = 'Resolution' + plotOverlay(subdirs, cached_histos, fitstr.format('Width'), props, outdir=args.odir) vars2D = { 'SimClustersEnFrac_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='Energy Fraction'), @@ -762,7 +919,7 @@ def __call__(self, parser, namespace, values, option_string=None): else: sub_folder = 'ParticleFlow' dqm_dir = f"DQMData/Run 1/HLT/Run summary/{sub_folder}/{matching}/CaloParticles" afile = ROOT.TFile.Open(args.file) - checkRootDir(afile, dqm_dir) + utils.checkRootDir(afile, dqm_dir) debug('Start caching PFCluster histograms...') subdirs = [] From 0920d7affbe19c0e414494a1555f8a4512b96583 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 10 Feb 2026 16:45:17 +0100 Subject: [PATCH 22/64] Add missing space. --- Validation/HGCalValidation/plugins/HGCalValidator.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Validation/HGCalValidation/plugins/HGCalValidator.cc b/Validation/HGCalValidation/plugins/HGCalValidator.cc index 89c8dc3a7a7a5..3b64d4e5ba1d8 100644 --- a/Validation/HGCalValidation/plugins/HGCalValidator.cc +++ b/Validation/HGCalValidation/plugins/HGCalValidator.cc @@ -423,7 +423,7 @@ void HGCalValidator::dqmAnalyze(const edm::Event& event, const auto& hits = event.get(hitsToken_); for (std::size_t index = 0; const auto& hgcRecHitCollection : hits) { if (hgcRecHitCollection->empty()) { - edm::LogWarning("HGCalValidator") << "HGCRecHitCollection #" << index << "is not valid."; + edm::LogWarning("HGCalValidator") << "HGCRecHitCollection #" << index << " is not valid."; } index++; } From d7d87e6ae148f9c74f561cc5356c6f65fa18fb1c Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 13 Feb 2026 19:45:54 +0100 Subject: [PATCH 23/64] [bugfix] Fix and add robustness to shared energy fraction computation. --- .../RecoParticleFlow/plugins/PFTester.cc | 219 +++++++++++------- .../scripts/makeHLTPFValidationPlots.py | 88 ++++--- 2 files changed, 184 insertions(+), 123 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index e385fb98a4076..3a942cbedc026 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -27,12 +27,15 @@ template class PFTesterT : public DQMEDAnalyzer { public: explicit PFTesterT(const edm::ParameterSet&); + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); protected: void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; void analyze(const edm::Event&, const edm::EventSetup&) override; std::string doubleToString(double x) const; - + double simClusterEnergy(const SimCluster& sc) const; + double recoClusterEnergyWeightedBySimFraction(const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const; + edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; edm::EDGetTokenT RechitToken_; @@ -170,7 +173,7 @@ class PFTesterT : public DQMEDAnalyzer { VU2Map h2d_simClustersMatchedRecoClusters_; U2Map h2d_recoClusters_; VU2Map h2d_recoClustersMatchedSimClusters_; - + VU2Map h2d_responsePt_; VU2Map h2d_responseE_; }; @@ -468,7 +471,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, ibook.setCurrentFolder(outFolder_ + "/PFCandidates"); h_PFCandEt_ = ibook.book1D("PFCandEt", "PFCandEt", 1000, 0, 1000); - h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -5, 5); + h_PFCandEta_ = ibook.book1D("PFCandEta", "PFCandEta", 200, -6, 6); h_PFCandPhi_ = ibook.book1D("PFCandPhi", "PFCandPhi", 200, -M_PI, M_PI); h_PFCandCharge_ = ibook.book1D("PFCandCharge", "PFCandCharge", 5, -2, 2); h_PFCandPdgId_ = ibook.book1D("PFCandPdgId", "PFCandPdgId", 44, -22, 22); @@ -507,8 +510,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- - // std::cout << std::endl; - // std::cout << "--- Analyze ---" << std::endl; + std::cout << std::endl; + std::cout << "--- Analyze --- " << std::endl; edm::Handle Rechit; iEvent.getByToken(RechitToken_, Rechit); if (!Rechit.isValid()) { @@ -586,27 +589,15 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // Fill map: for each simCluster, the energy of the caloParticle computed as the sum of all simClusters arising from it double energySumSimClusters = 0; double energySumSimHits = 0; - double energyFracSumSimHits = 0; - + double recoEnergySumWeightedBySimFrac = 0; for (const auto& scRef : caloParticles[cpId].simClusters()) { auto const& sc = *(scRef); // Compute energy of caloParticle as sum of simClusters energies (from SimTrack energy) energySumSimClusters += sc.energy(); // Compute energy of caloParticle as sum of all hits from all simClusters - for (auto hit_energy : sc.hits_and_energies()) { - energySumSimHits += hit_energy.second; - } + energySumSimHits += simClusterEnergy(sc); // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters - for (auto hit_fraction : sc.hits_and_fractions()) { - DetId id(hit_fraction.first); - auto rechitIt = - std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); - if (rechitIt == pfRechit.end()) { - continue; - } else { - energyFracSumSimHits += rechitIt->energy() * hit_fraction.second; - } - } + recoEnergySumWeightedBySimFrac += recoClusterEnergyWeightedBySimFraction(sc, pfRechit); } for (const auto& scRef : caloParticles[cpId].simClusters()) { simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; @@ -615,7 +606,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e edm::LogPrint("PFTester") << " caloParticle [" << cpId << "]: energy=" << caloParticles[cpId].energy() << ", energySumSimClusters=" << energySumSimClusters << ", energySumSimHits=" << energySumSimHits - << ", energyFracSumSimHits=" << energyFracSumSimHits << std::endl; + << ", recoEnergySumWeightedBySimFrac=" << recoEnergySumWeightedBySimFrac << std::endl; #endif h_CPToSCEnergyFraction_->Fill(energySumSimClusters / caloParticles[cpId].energy()); @@ -634,12 +625,12 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e #ifdef debug edm::LogPrint("PFTester") << " caloParticle [" << cpId << "] matched to RecoCluster [" << recoPair.first.index() << "] with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", shared energy fraction: " << recoPair.second.first / recoEnergySumWeightedBySimFrac << ", score: " << recoPair.second.second << std::endl; #endif h_CP_simToRecoScore_->Fill(recoPair.second.second); - h_CP_simToRecoShEnF_->Fill(recoPair.second.first / energyFracSumSimHits); - h_CP_simToRecoShEnF_Score_->Fill(recoPair.second.first / energyFracSumSimHits, recoPair.second.second); + h_CP_simToRecoShEnF_->Fill(recoPair.second.first / recoEnergySumWeightedBySimFrac); + h_CP_simToRecoShEnF_Score_->Fill(recoPair.second.first / recoEnergySumWeightedBySimFrac, recoPair.second.second); } } @@ -660,27 +651,13 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ----- Efficiency and split computation at cluster level ------------ // -------------------------------------------------------------------- - uint nSimClusters = 0; uint nSimClustersPrimary = 0; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = 0; - for (auto hit_energy : simClusters[simId].hits_and_energies()) { - energySumSimHits += hit_energy.second; - } + double energySumSimHits = simClusterEnergy(simClusters[simId]); h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simClusters[simId].energy()); - double energyFracSumSimHits = 0; - for (auto hit_energy : simClusters[simId].hits_and_fractions()) { - DetId id(hit_energy.first); - auto rechitIt = - std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); - if (rechitIt == pfRechit.end()) { - continue; - } else { - energyFracSumSimHits += rechitIt->energy() * hit_energy.second; - } - } + double recoEnergySumWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; @@ -761,7 +738,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", shared energy fraction: " << recoPair.second.first / recoEnergySumWeightedBySimFrac << ", score: " << recoPair.second.second << ", hits="; for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { DetId id(hit_energy.recHitRef()->detId()); @@ -774,7 +751,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e auto score = recoPair.second.second; auto shared_energy = recoPair.second.first; - auto shared_energy_frac = shared_energy / energyFracSumSimHits; + auto shared_energy_frac = shared_energy / recoEnergySumWeightedBySimFrac; h_simToRecoScore_->Fill(score); h_simToRecoShEnF_->Fill(shared_energy_frac); @@ -859,7 +836,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ----- Fakes and merge computation at cluster level ----------------- // -------------------------------------------------------------------- - h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { // fake and merge denominator @@ -870,6 +846,18 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); + if (abs(recoClusters[recoId].eta()) > 1.35) { + std::cout << "============== RECO CLUSTER ANOMALY ========= " << std::endl; + std::cout << recoClusters[recoId].eta() << std::endl; + std::cout << recoClusters[recoId].energy() << std::endl; + for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { + DetId idid = haf.first; + if(idid.det() != DetId::Ecal or idid.subdetId() != EcalBarrel) { + std::cout << idid.det() << " < " << idid.subdetId() << std::endl; + } + } + std::cout << "============================================= " << std::endl; + } h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); @@ -901,10 +889,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e << std::endl; #endif - double energySumSimHits = 0; - for (auto hit_energy : simClusters[simPairIdx].hits_and_energies()) { - energySumSimHits += hit_energy.second; - } + double energySumSimHits = simClusterEnergy(simClusters[simPairIdx]); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simPairIdx]; @@ -973,13 +958,13 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ----- Cluster response computation --------------------------------- // -------------------------------------------------------------------- - // std::cout << std::endl; - // std::cout << "--- Event " << iEvent.eventAuxiliary().event() << " ---" << std::endl; + std::cout << std::endl; + std::cout << "--- Event " << iEvent.eventAuxiliary().event() << " ---" << std::endl; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = 0; - for (auto hit_energy : simClusters[simId].hits_and_energies()) { - energySumSimHits += hit_energy.second; - } + double energySumSimHits = simClusterEnergy(simClusters[simId]); + + double recoEnergyWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); + // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; if (SimClusterToCPEnergyFraction < enFracCut_) @@ -1024,42 +1009,55 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e } else { // cut on shared energy fraction double shared_energy = recoPair.second.first; - double shared_energy_frac = shared_energy / energySumSimHits; + double shared_energy_frac = shared_energy / recoEnergyWeightedBySimFrac; passMatch = shared_energy_frac > thresh; } - // std::cout << "===============================" << std::endl; - // std::cout << "matchByScore? " << doMatchByScore_ << std::endl; - // std::cout << "passMatch: " << passMatch << ", recoId: " << recoId << std::endl; - // std::cout << "sim en: " << energySumSimHits << ", reco en: " << recoClusters[recoId].energy() << std::endl; - // std::cout << "sim eta: " << simClusters[simId].eta() << ", reco eta: " << recoClusters[recoId].eta() << ", sim track eta: " << simTrackEtaAtBoundary << std::endl; - // std::cout << "sim phi: " << simClusters[simId].phi() << ", reco phi: " << recoClusters[recoId].phi() << std::endl; - // std::cout << "score: " << recoPair.second.second << std::endl; - // std::cout << "shared en frac: " << recoPair.second.first / energySumSimHits << std::endl; - // std::cout << "n sim clusters: " << simClusters.size() << std::endl; - // std::cout << "n matched reco clusters: " << simToRecoMatchedSorted.size() << std::endl; - // for (const auto& recoPairDebug : simToRecoMatchedSorted) { - // std::cout << "- score: " << recoPairDebug.second.second << ", share en frac: " << recoPairDebug.second.first / energySumSimHits << ", en: " << recoClusters[recoPairDebug.first.index()].energy() << std::endl; - // } - // std::cout << "threshold: " << thresh << std::endl; - // std::cout << "===============================" << std::endl; + std::cout << "===============================" << std::endl; + std::cout << "matchByScore? " << doMatchByScore_ << std::endl; + std::cout << "passMatch: " << passMatch << ", recoId: " << recoId << std::endl; + std::cout << "sim en: " << energySumSimHits << ", reco en: " << recoClusters[recoId].energy() << std::endl; + std::cout << "sim en frac: " << recoEnergyWeightedBySimFrac << std::endl; + std::cout << "sim eta: " << simClusters[simId].eta() << ", reco eta: " << recoClusters[recoId].eta() << ", sim track eta: " << simTrackEtaAtBoundary << std::endl; + std::cout << "sim phi: " << simClusters[simId].phi() << ", reco phi: " << recoClusters[recoId].phi() << std::endl; + std::cout << "score: " << recoPair.second.second << std::endl; + std::cout << "shared en frac: " << recoPair.second.first / energySumSimHits << std::endl; + std::cout << "n sim clusters: " << simClusters.size() << std::endl; + std::cout << "n matched reco clusters: " << simToRecoMatchedSorted.size() << std::endl; + for (const auto& recoPairDebug : simToRecoMatchedSorted) { + std::cout << "- score: " << recoPairDebug.second.second << ", share en frac: " << recoPairDebug.second.first / energySumSimHits << ", en: " << recoClusters[recoPairDebug.first.index()].energy() << std::endl; + } + std::cout << "threshold: " << thresh << std::endl; + for (auto hae : simClusters[simId].hits_and_energies()) { + DetId idid(hae.first); + if(idid.det() != DetId::Ecal) { + std::cout << "sc ERROR 1!!!!!!!!!!!!" << std::endl; + } + if(idid.subdetId() != EcalBarrel) { + std::cout << "sc ERROR 2!!!!!!!!!!!!" << std::endl; + } + } + for (auto pfrh : pfRechit) { + DetId idid(pfrh.detId()); + if(idid.det() != DetId::Ecal) { + std::cout << "pfhit ERROR 1!!!!!!!!!!!!" << std::endl; + } + if(idid.subdetId() != EcalBarrel) { + std::cout << "pfhit ERROR 2!!!!!!!!!!!!" << std::endl; + } + } + for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { + DetId idid = haf.first; + if(idid.det() != DetId::Ecal) { + std::cout << "rc ERROR 1!!!!!!!!!!!!" << std::endl; + } + if(idid.subdetId() != EcalBarrel) { + std::cout << "rc ERROR 2!!!!!!!!!!!!" << std::endl; + } + } + std::cout << "===============================" << std::endl; if (passMatch) { - // h2d_responsePt_[ithr]["En"]->Fill(energySumSimHits, - // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, - // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), - // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["Pt"]->Fill(simClusters[simId].pt(), - // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, - // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["Phi"]->Fill(simClusters[simId].phi(), - // recoClusters[recoId].pt() / simClusters[simId].pt()); - // h2d_responsePt_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), - // recoClusters[recoId].pt() / simClusters[simId].pt()); - h2d_responseE_[ithr]["En"]->Fill(energySumSimHits, recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, @@ -1071,8 +1069,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].energy() / energySumSimHits); - // std::cout << "fill response: " << recoClusters[recoId].energy() / energySumSimHits << std::endl; - // std::cout << "============== break =================" << std::endl; + std::cout << "fill response: " << recoClusters[recoId].energy() / energySumSimHits << std::endl; + std::cout << "============== break =================" << std::endl; break; } } @@ -1082,7 +1080,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ---------------- PF Candidates ------------------------------------- // -------------------------------------------------------------------- - const reco::PFCandidateCollection* pf_candidates; edm::Handle PFCand; iEvent.getByToken(PFCandToken_, PFCand); @@ -1100,7 +1097,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // -------------------- PF Blocks and Elements ------------------------ // -------------------------------------------------------------------- - // Loop Over Particle Flow Candidates for (size_t i = 0; i < pf_candidates->size(); ++i) { const auto& particle = (*pf_candidates)[i]; @@ -1213,6 +1209,53 @@ std::string PFTesterT::doubleToString(double x) const { return xnew; } +// compute the total energy of a simulated cluster +template +double PFTesterT::simClusterEnergy(const SimCluster& sc) const { + double energySumSimHits = 0; + for (auto hit_energy : sc.hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + return energySumSimHits; +} + +// compute the denominator of the shared energy fraction, i.e, +// the total energy of a reconstructed cluster, weighted by the sim cluster energy fraction +template +double PFTesterT::recoClusterEnergyWeightedBySimFraction(const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const { + double recoEnergySumWeightedBySimFrac = 0; + for (auto hit_fraction : sc.hits_and_fractions()) { + DetId id(hit_fraction.first); + auto rechitIt = + std::find_if(pfrechits.begin(), pfrechits.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + if (rechitIt == pfrechits.end()) { + continue; + } else { + recoEnergySumWeightedBySimFrac += rechitIt->energy() * hit_fraction.second; + } + } + return recoEnergySumWeightedBySimFrac; +} + +template +void PFTesterT::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("outFolder", "HLT/ParticleFlow"); + desc.add("PFCand", edm::InputTag("hltParticleFlow")); + desc.add("Rechit", edm::InputTag("hltParticleFlowRecHitECALUnseeded")); + desc.add("RecoCluster", edm::InputTag("hltParticleFlowClusterECALUnseeded")); + desc.add("SimCluster", edm::InputTag("mix", "MergedCaloTruth")); + desc.add("CaloParticle", edm::InputTag("mix","MergedCaloTruth")); + desc.add("ClusterSimClusterAssociator", edm::InputTag("hltPFClusterSimClusterAssociationProducerECAL")); + desc.add("ClusterCaloParticleAssociator", edm::InputTag("hltPFClusterCaloParticleAssociationProducerECAL")); + desc.add>("assocScoreThresholds", {0.1}); + desc.add("doMatchByScore", true); + desc.add("enFracCut", 0.01); + desc.add("ptCut", 0.1); + desc.add("etaCut", 3.0); + descriptions.addWithDefaultLabel(desc); +} + using PFClusterTester = PFTesterT; using CaloClusterTester = PFTesterT; diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 269812b11d63c..8a936a74568ee 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -107,12 +107,12 @@ class InputArgs: normalize: bool = False class Plotter: - def __init__(self, label, fontsize=18, grid_color='grey'): + def __init__(self, label, fontsize=18, era='Phase2', grid_color='grey'): self._fig, self._ax = plt.subplots(figsize=(10, 10)) self.fontsize = fontsize - if args.era == 'Phase2': era='Phase-2'; en='14' - elif args.era == 'Run3': era='Run-3'; en='13.6' + if era == 'Phase2': era='Phase-2'; en='14' + elif era == 'Run3': era='Run-3'; en='13.6' hep.cms.text(f' {era} Simulation Preliminary', ax=self._ax, fontsize=fontsize) hep.cms.lumitext(label + f" | {en} TeV", ax=self._ax, fontsize=fontsize) if grid_color: @@ -176,7 +176,7 @@ def save(self, name): plt.savefig(name + '.' + ext) plt.close() -def plotProject(h, props, rebin_edges, outname): +def plotProject(h, sample_label, era, props, rebin_edges, outname): """ Project and plot slices of a 2D histogram. """ @@ -184,7 +184,7 @@ def plotProject(h, props, rebin_edges, outname): valuesList, errorsList = [], [] fit_params = {} if props.fit else None - plotter = Plotter(args.sample_label, grid_color=None) + plotter = Plotter(sample_label, grid_color=None, era=era) for ibin, (low, high) in enumerate(zip(rebin_edges[:-1],rebin_edges[1:])): hproj = h.ProjectionY(h.GetName() + "_proj" + str(ibin), @@ -225,17 +225,17 @@ def plotProject(h, props, rebin_edges, outname): plotter.save(outname) return fit_params -def plotOverlay(subdirs, cached_histos, name, props, outdir): +def plotOverlay(subdirs, cached_histos, name, match_by_score, sample_label, era, props, outdir): """ Plots 1D distributions, overlaying plots with identical names in different 'subdirs'. """ colors_iter = iter(colorblind_palette) pattern = r"Score(\d+)p(\d+)" - matching = "score" if args.match_by_score else "shared energy fraction" + matching = "score" if match_by_score else "shared energy fraction" replacement = lambda m: f"{matching} = {m.group(1)}.{m.group(2)}" - plotter = Plotter(args.sample_label, grid_color=None) + plotter = Plotter(sample_label, grid_color=None, era=era) for sub in subdirs: root_hist = cached_histos[f"{sub}/{name}"] @@ -280,16 +280,16 @@ def plotOverlay(subdirs, cached_histos, name, props, outdir): plt.tight_layout() plotter.save( os.path.join(outdir, name) ) -def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): +def plotOverlayRatio(subdirs, cached_histos, num, den, match_by_score, sample_label, era, props, outdir): """ Plots 1D distributions of numerator / denominator. """ colors_iter = iter(colorblind_palette) pattern = r"Score(\d+)p(\d+)" - matching = "score" if args.match_by_score else "shared energy fraction" + matching = "score" if match_by_score else "shared energy fraction" replacement = lambda m: f"{matching} = {m.group(1)}.{m.group(2)}" - plotter = Plotter(args.sample_label, grid_color=None) + plotter = Plotter(sample_label, grid_color=None, era=era) for sub in subdirs: hist_num = cached_histos[f"{sub}/{num}"] hist_den = cached_histos[f"{sub}/{den}"] @@ -336,11 +336,11 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir): plt.tight_layout() plotter.save( os.path.join(outdir, props.name) ) -def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, suffix=''): +def plotEffComp1D(cached_histos, title, vars1d, outdir, text, sample_label, era, top_text=False, suffix=''): """ Plots 1D distributions. """ - plotter = Plotter(args.sample_label, grid_color=None) + plotter = Plotter(sample_label, grid_color=None, era=era) ax2 = plotter.ax.twinx() eff_color = '#bd1f01' @@ -412,6 +412,7 @@ def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, su errorsList.append(errors) plotter.limits_with_margin(valuesList, errorsList, logY=logy) + #plotter.limits(x=(-9,109), y=(10E-5, 10E4), logY=logy) plotter.labels(x=xlabel, y=ylabel, legend_title='') plotter.ax.text(0.03, 0.97, text, transform=plotter.ax.transAxes, fontsize=fontsize, @@ -429,12 +430,12 @@ def plotEffComp1D(cached_histos, title, vars1d, outdir, text, top_text=False, su plt.tight_layout() plotter.save( os.path.join(outdir, title) ) -def plot2D(h, props, outname): +def plot2D(h, sample_label, era, props, outname): """ Plot with mplhep's hist2d (preserves ROOT bin edges, color bar included) empty bins will be invisible (background color). """ - plotter = Plotter(args.sample_label, fontsize=15) + plotter = Plotter(sample_label, fontsize=15, era=era) nbins_x, nbins_y, x_edges, y_edges = define_bins_2D(h) values = histo_values_2D(h) @@ -460,9 +461,9 @@ def plot2D(h, props, outname): plt.tight_layout() plotter.save(outname) -def plot1D(h, props, outname): +def plot1D(h, sample_label, era, props, outname): - plotter = Plotter(args.sample_label, fontsize=15) + plotter = Plotter(sample_label, fontsize=15, era=era) nbins, bin_edges, bin_centers, bin_widths = define_bins(h) values, errors = histo_values_errors(h) errors /= 2 @@ -667,7 +668,8 @@ def __call__(self, parser, namespace, values, option_string=None): # Compare pairs of variables for title, props in varsDict.items(): - plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') + plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', + sample_label=args.sample_label, era=args.era, suffix=f'') varsDict = { # Cluster fake rate @@ -675,31 +677,31 @@ def __call__(self, parser, namespace, values, option_string=None): ratio=f'{subdir}/Fake_vs_En', den=f'RecoClustersEn', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersEn', legnum='Matched RecoClusters', - xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4 + xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=True, ), f'{subdir}/Fake_vs_Pt': InputArgs( ratio=f'{subdir}/Fake_vs_Pt', den=f'RecoClustersPt', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersPt', legnum='Matched RecoClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4 + xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, ), f'{subdir}/Fake_vs_Eta': InputArgs( ratio=f'{subdir}/Fake_vs_Eta', den=f'RecoClustersEta', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersEta', legnum='Matched RecoClusters', - xtitle=r'$\eta$', ytitle=nPFClustersLabel + xtitle=r'$\eta$', ytitle=nPFClustersLabel, ), f'{subdir}/Fake_vs_Phi': InputArgs( ratio=f'{subdir}/Fake_vs_Phi', den=f'RecoClustersPhi', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersPhi', legnum='Matched RecoClusters', - xtitle=r'$\phi$', ytitle=nPFClustersLabel + xtitle=r'$\phi$', ytitle=nPFClustersLabel, ), f'{subdir}/Fake_vs_Mult': InputArgs( ratio=f'{subdir}/Fake_vs_Mult', den=f'RecoClustersMult', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', - xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4 + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, ), # Cluster merge rate (WIP) f'{subdir}/Merge_vs_En': InputArgs( @@ -735,7 +737,8 @@ def __call__(self, parser, namespace, values, option_string=None): # Compare pairs of variables for title, props in varsDict.items(): - plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', suffix=f'') + plotEffComp1D(cached_histos, title, vars1d=props, outdir=args.odir, text='', + sample_label=args.sample_label, era=args.era, suffix=f'') varsOverlay = { "ResponseE_En_Mean" : InputArgs(ytitle=titles['response'], rebin=(0., 5., 10., 20., 40., 60., 100.), @@ -774,7 +777,7 @@ def __call__(self, parser, namespace, values, option_string=None): "Merge_vs_Mult" : InputArgs(ytitle=titles['merge'], rebin=4, xtitle='Multiplicity'), } for name, props in varsOverlay.items(): - plotOverlay(subdirs, cached_histos, name, props, outdir=args.odir) + plotOverlay(subdirs, cached_histos, name, args.match_by_score, args.sample_label, args.era, props, outdir=args.odir) varsResponse = { ("ResponseE_En_Sigma", "ResponseE_En_Mean"): @@ -811,7 +814,7 @@ def __call__(self, parser, namespace, values, option_string=None): ), } for (num, den), props in varsResponse.items(): - plotOverlayRatio(subdirs, cached_histos, num, den, props, outdir=args.odir) + plotOverlayRatio(subdirs, cached_histos, num, den, args.match_by_score, args.sample_label, args.era, props, outdir=args.odir) vars2DProjection = { **{f'{subdir}/ResponseE_En': @@ -837,7 +840,8 @@ def __call__(self, parser, namespace, values, option_string=None): **{f'{subdir}/ResponseE_Eta': InputArgs( xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', - rebin=(-1.5, -1.3, -1., -0.75, -0.5, -0.25, 0., 0.25, 0.5, 0.75, 1., 1.3, 1.5) + # rebin=(-1.5, -1.3, -1., -0.75, -0.5, -0.25, 0., 0.25, 0.5, 0.75, 1., 1.3, 1.5) + rebin=(-1.5, -0.75, 0., 0.75, 1.5) ) for subdir in subdirs}, **{f'{subdir}/ResponseE_Phi': InputArgs( @@ -858,7 +862,8 @@ def __call__(self, parser, namespace, values, option_string=None): for name, props in vars2DProjection.items(): root_hist = cached_histos[f"{name}"] - fitpars = plotProject(root_hist, props, rebin_edges=props.rebin, outname=os.path.join(args.odir, name + '_Projected')) + fitpars = plotProject(root_hist, args.sample_label, args.era, props, rebin_edges=props.rebin, + outname=os.path.join(args.odir, name + '_Projected')) if props.fit: n_bins = len(fitpars) @@ -880,12 +885,20 @@ def __call__(self, parser, namespace, values, option_string=None): if props.fit: props.xtitle = 'Energy [GeV]' props.ytitle = 'Response' - plotOverlay(subdirs, cached_histos, fitstr.format('Mean'), props, outdir=args.odir) + plotOverlay(subdirs, cached_histos, fitstr.format('Mean'), args.match_by_score, args.sample_label, args.era, props, outdir=args.odir) props.ytitle = 'Resolution' - plotOverlay(subdirs, cached_histos, fitstr.format('Width'), props, outdir=args.odir) + plotOverlay(subdirs, cached_histos, fitstr.format('Width'), args.match_by_score, args.sample_label, args.era, props, outdir=args.odir) vars2D = { - 'SimClustersEnFrac_Mult': dict(ytitle='Multiplicity', var='# Clusters', xtitle='Energy Fraction'), + 'SimClustersEnFrac_Mult': dict(ytitle='Multiplicity', var='# Sim Clusters', xtitle='Energy Fraction'), + 'SimClustersEn_Mult': dict(ytitle='Multiplicity', var='# Sim Clusters', xtitle='Energy [GeV]'), + 'SimClustersEn_Eta': dict(ytitle=r'$\eta$', var='# Sim Clusters', xtitle='Energy [GeV]'), + 'SimClustersEn_Phi': dict(ytitle=r'$\phi$', var='# Sim Clusters', xtitle='Energy [GeV]'), + 'SimClustersMult_Eta': dict(xtitle='Multiplicity', var='# Sim Clusters', ytitle=r'$\eta$'), + 'RecoClustersEn_Mult': dict(ytitle='Multiplicity', var='# Reco Clusters', xtitle='Energy [GeV]'), + 'RecoClustersEn_Eta': dict(ytitle=r'$\eta$', var='# Reco Clusters', xtitle='Energy [GeV]', logz=True), + 'RecoClustersEn_Phi': dict(ytitle=r'$\phi$', var='# Reco Clusters', xtitle='Energy [GeV]', logz=True), + 'RecoClustersMult_Eta': dict(xtitle='Multiplicity', var='# Reco Clusters', ytitle=r'$\eta$'), 'simToRecoScore_En': dict(ytitle='SimCluster Energy [GeV]', var='# Clusters', xtitle='SimToReco Score'), 'simToRecoScore_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Score'), 'simToRecoScore_EnSimTrack': dict(ytitle='SimTrack Energy [GeV]', var='# Clusters', xtitle='SimToReco Score'), @@ -897,9 +910,14 @@ def __call__(self, parser, namespace, values, option_string=None): 'simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# Clusters', xtitle='SimToReco Shared Energy Fraction', logz=True), } + vars2D.update({ + **{f'{subdir}/ResponseE_Eta': dict(ytitle=titles['response'], var='# Clusters', xtitle=r'$\eta$') for subdir in subdirs}, + **{f'{subdir}/ResponseE_Phi': dict(ytitle=titles['response'], var='# Clusters', xtitle=r'$\phi$') for subdir in subdirs}, + }) + for name, props in vars2D.items(): root_hist = cached_histos[f"{name}"] - plot2D(root_hist, props, outname=os.path.join(args.odir, name)) + plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) vars1D = { 'simToRecoShEnF': dict(xtitle='SimToReco Shared Energy Fraction', ytitle='# SimClusters', var='SimClusters', logy=False), @@ -909,7 +927,7 @@ def __call__(self, parser, namespace, values, option_string=None): for name, props in vars1D.items(): root_hist = cached_histos[f"{name}"] - plot1D(root_hist, props, outname=os.path.join(args.odir, name)) + plot1D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) ################################################################################## # Temporary hack to access CaloParticle histrograms @@ -946,7 +964,7 @@ def __call__(self, parser, namespace, values, option_string=None): for name, props in vars1D.items(): root_hist = cached_histos[f"{name}"] - plot1D(root_hist, props, outname=os.path.join(args.odir, name)) + plot1D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) vars2D = { 'CP_simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# CaloParticles', xtitle='SimToReco Shared Energy Fraction', logz=True), @@ -954,4 +972,4 @@ def __call__(self, parser, namespace, values, option_string=None): for name, props in vars2D.items(): root_hist = cached_histos[f"{name}"] - plot2D(root_hist, props, outname=os.path.join(args.odir, name)) + plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) From 8a2091fd9b4d4baf3c6b4f70414250332e459c37 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Wed, 18 Feb 2026 19:19:11 +0100 Subject: [PATCH 24/64] Add RecHit and Digi tester. --- .../RecoParticleFlow/plugins/DigisTester.cc | 147 +++++++++++ .../RecoParticleFlow/plugins/RecHitTester.cc | 240 ++++++++++++++++++ .../scripts/makeHLTHitValidationPlots.py | 137 ++++++++++ 3 files changed, 524 insertions(+) create mode 100644 Validation/RecoParticleFlow/plugins/DigisTester.cc create mode 100644 Validation/RecoParticleFlow/plugins/RecHitTester.cc create mode 100644 Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py diff --git a/Validation/RecoParticleFlow/plugins/DigisTester.cc b/Validation/RecoParticleFlow/plugins/DigisTester.cc new file mode 100644 index 0000000000000..eaeecc0d76a43 --- /dev/null +++ b/Validation/RecoParticleFlow/plugins/DigisTester.cc @@ -0,0 +1,147 @@ +#include "FWCore/Framework/interface/MakerMacros.h" +#include "DQMServices/Core/interface/DQMEDAnalyzer.h" +#include "DQMServices/Core/interface/DQMStore.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "DataFormats/Common/interface/Handle.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "Geometry/CaloGeometry/interface/CaloGeometry.h" +#include "Geometry/Records/interface/CaloGeometryRecord.h" +#include "DataFormats/GeometryVector/interface/GlobalPoint.h" +#include "Geometry/CaloTopology/interface/EcalBarrelTopology.h" +#include "Geometry/CaloTopology/interface/EcalEndcapTopology.h" + +#include "DataFormats/EcalDigi/interface/EcalDigiCollections.h" +#include "DataFormats/DetId/interface/DetId.h" +#include "DataFormats/EcalDetId/interface/EBDetId.h" + +class DigisTester : public DQMEDAnalyzer { +public: + explicit DigisTester(const edm::ParameterSet&); + +protected: + void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; + void analyze(const edm::Event&, const edm::EventSetup&) override; + + edm::ESGetToken geometry_token_; + edm::EDGetTokenT ecalEBDigisToken_; + edm::EDGetTokenT ecalEEDigisToken_; + + std::string outFolder_; + + using U2Map = std::unordered_map; + U2Map h2d_ebdigis_, h2d_eedigis_; +}; + +DigisTester::DigisTester(const edm::ParameterSet& iConfig) + : geometry_token_(esConsumes()), + ecalEBDigisToken_(consumes(iConfig.getParameter("ecalEBDigis"))), + ecalEEDigisToken_(consumes(iConfig.getParameter("ecalEEDigis"))), + outFolder_(iConfig.getParameter("outFolder")) {} + +void DigisTester::bookHistograms(DQMStore::IBooker& ibook, + edm::Run const&, + edm::EventSetup const&) { + ibook.setCurrentFolder(outFolder_ + "/Digis"); + + unsigned mNBinsADC = 100; + float mADCMin = 180.; + float mADCMax = 500.; + unsigned mNBinsPhi = 100; + float mPhiMax = 3.2; + unsigned mNBinsEta = 100; + float mEtaMax = 3.3; + + std::unordered_map> ebVars = { + {"ADC_Eta", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsEta, -mEtaMax, mEtaMax)}, + {"ADC_Phi", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + {"Eta_Phi", std::make_tuple(mNBinsEta, -mEtaMax, mEtaMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + }; + + for (auto& ebVar : ebVars) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = ebVar.second; + auto x_title = ebVar.first.substr(0, ebVar.first.find("_")); + auto y_title = ebVar.first.substr(ebVar.first.find("_") + 1); + h2d_ebdigis_[ebVar.first] = ibook.book2D("EcalEBDigis" + ebVar.first, + "EcalEBDigis;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + } + + std::unordered_map> eeVars = { + {"ADC_Eta", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsEta, -mEtaMax, mEtaMax)}, + {"ADC_Phi", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + {"Eta_Phi", std::make_tuple(mNBinsEta, -mEtaMax, mEtaMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + }; + + for (auto& eeVar : eeVars) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = eeVar.second; + auto x_title = eeVar.first.substr(0, eeVar.first.find("_")); + auto y_title = eeVar.first.substr(eeVar.first.find("_") + 1); + h2d_eedigis_[eeVar.first] = ibook.book2D("EcalEEDigis" + eeVar.first, + "EcalEEDigis;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + } +} + +void DigisTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + // const auto& barrelGeom = caloGeom.getSubdetectorGeometry(DetId::Ecal, EcalBarrel); + // const auto& endcapGeom = caloGeom.getSubdetectorGeometry(DetId::Ecal, EcalEndcap); + + edm::Handle ebDigisHandle; + iEvent.getByToken(ecalEBDigisToken_, ebDigisHandle); + if (!ebDigisHandle.isValid()) { + edm::LogPrint("DigisTester") << "Input EB Digis collection not found."; + return; + } + + edm::Handle eeDigisHandle; + iEvent.getByToken(ecalEEDigisToken_, eeDigisHandle); + if (!eeDigisHandle.isValid()) { + edm::LogPrint("DigisTester") << "Input EE Digis collection not found."; + return; + } + + for (const edm::DataFrame& digi : *ebDigisHandle) { + float eta = caloGeom.getPosition(digi.id()).eta(); + float phi = caloGeom.getPosition(digi.id()).phi(); + + // Get the number of ADC samples (usually 10) + int nADCSamples = digi.size(); + + float adcMax = 0; + for (int i=0; i(digi).sample(i).adc(); + if (adcVal > adcMax) adcMax = adcVal; + } + + h2d_ebdigis_["ADC_Eta"]->Fill(adcMax, eta); + h2d_ebdigis_["ADC_Phi"]->Fill(adcMax, phi); + h2d_ebdigis_["Eta_Phi"]->Fill(eta, phi); + } + + for (const edm::DataFrame& digi : *eeDigisHandle) { + float eta = caloGeom.getPosition(digi.id()).eta(); + float phi = caloGeom.getPosition(digi.id()).phi(); + + // Get the number of ADC samples (usually 10) + int nADCSamples = digi.size(); + + float adcMax = 0; + for (int i=0; i(digi).sample(i).adc(); + if (adcVal > adcMax) adcMax = adcVal; + } + + h2d_eedigis_["ADC_Eta"]->Fill(adcMax, eta); + h2d_eedigis_["ADC_Phi"]->Fill(adcMax, phi); + h2d_eedigis_["Eta_Phi"]->Fill(eta, phi); + } +} + +DEFINE_FWK_MODULE(DigisTester); diff --git a/Validation/RecoParticleFlow/plugins/RecHitTester.cc b/Validation/RecoParticleFlow/plugins/RecHitTester.cc new file mode 100644 index 0000000000000..a19a778cbd3ff --- /dev/null +++ b/Validation/RecoParticleFlow/plugins/RecHitTester.cc @@ -0,0 +1,240 @@ +#include "FWCore/Framework/interface/MakerMacros.h" +#include "DQMServices/Core/interface/DQMEDAnalyzer.h" +#include "DQMServices/Core/interface/DQMStore.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "DataFormats/Common/interface/Handle.h" + +#include "DataFormats/EcalRecHit/interface/EcalRecHitCollections.h" +#include "DataFormats/ParticleFlowReco/interface/PFRecHit.h" +#include "SimDataFormats/CaloHit/interface/PCaloHit.h" + +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include + +#include "Geometry/CaloGeometry/interface/CaloGeometry.h" +#include "Geometry/Records/interface/CaloGeometryRecord.h" +#include "DataFormats/GeometryVector/interface/GlobalPoint.h" +#include "Geometry/CaloTopology/interface/EcalBarrelTopology.h" + +class RecHitTester : public DQMEDAnalyzer { +public: + explicit RecHitTester(const edm::ParameterSet&); + static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); + +protected: + void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; + void analyze(const edm::Event&, const edm::EventSetup&) override; + +private: + using SimHitsT = std::vector; + using RecHitsT = EcalRecHitCollection; + using PFRecHitsT = reco::PFRecHitCollection; + using UncalibRecHitsT = EcalUncalibratedRecHitCollection; + + edm::ESGetToken caloGeomToken_; + + const edm::EDGetTokenT ebSimHitToken_, eeSimHitToken_; + const edm::EDGetTokenT ebUncalibRecHitToken_, eeUncalibRecHitToken_; + const edm::EDGetTokenT ebRecHitToken_, eeRecHitToken_; + const edm::EDGetTokenT PFRecHitToken_; + + std::string outFolder_; + + std::unordered_map> histo2dVarsReco = { + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + }; + + using U2Map = std::unordered_map; + U2Map h2d_ebsimHits_, h2d_eesimHits_; + U2Map h2d_ebuncalibRecHits_, h2d_eeuncalibRecHits_; + U2Map h2d_ebrecHits_, h2d_eerecHits_; + U2Map h2d_pfRecHits_; +}; + +RecHitTester::RecHitTester(const edm::ParameterSet& iConfig) + : caloGeomToken_(esConsumes()), + ebSimHitToken_(consumes(iConfig.getParameter("ebSimHits"))), + eeSimHitToken_(consumes(iConfig.getParameter("eeSimHits"))), + ebUncalibRecHitToken_(consumes(iConfig.getParameter("ebUncalibRecHits"))), + eeUncalibRecHitToken_(consumes(iConfig.getParameter("eeUncalibRecHits"))), + ebRecHitToken_(consumes(iConfig.getParameter("ebRecHits"))), + eeRecHitToken_(consumes(iConfig.getParameter("eeRecHits"))), + PFRecHitToken_(consumes(iConfig.getParameter("pfRecHits"))), + outFolder_(iConfig.getParameter("outFolder")) {} + +void RecHitTester::bookHistograms(DQMStore::IBooker& ibook, + edm::Run const&, + edm::EventSetup const&) { + + ibook.setCurrentFolder(outFolder_ + "/Hits"); + + for (auto& h2dVar : histo2dVarsReco) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); + h2d_ebsimHits_[h2dVar.first] = ibook.book2D("EBSimHits" + h2dVar.first, + "EBSimHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + h2d_eesimHits_[h2dVar.first] = ibook.book2D("EESimHits" + h2dVar.first, + "EESimHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + h2d_ebuncalibRecHits_[h2dVar.first] = ibook.book2D("EBUncalibRecHits" + h2dVar.first, + "EBUncalibRecHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + h2d_eeuncalibRecHits_[h2dVar.first] = ibook.book2D("EEUncalibRecHits" + h2dVar.first, + "EEUncalibRecHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + h2d_ebrecHits_[h2dVar.first] = ibook.book2D("EBRecHits" + h2dVar.first, + "EBRecHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + h2d_eerecHits_[h2dVar.first] = ibook.book2D("EERecHits" + h2dVar.first, + "EERecHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + h2d_pfRecHits_[h2dVar.first] = ibook.book2D("PFRecHits" + h2dVar.first, + "PFRecHits;" + x_title + ";" + y_title, + nBinsX, hMinX, hMaxX, + nBinsY, hMinY, hMaxY); + } +} + +void RecHitTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { + const auto& caloGeom = iSetup.getData(caloGeomToken_); + + edm::Handle ebsimhitHandle; + iEvent.getByToken(ebSimHitToken_, ebsimhitHandle); + if (!ebsimhitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input EB SimHit collection not found."; + return; + } + edm::Handle eesimhitHandle; + iEvent.getByToken(eeSimHitToken_, eesimhitHandle); + if (!eesimhitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input EE SimHit collection not found."; + return; + } + + edm::Handle ebuncalibrechitHandle; + iEvent.getByToken(ebUncalibRecHitToken_, ebuncalibrechitHandle); + if (!ebuncalibrechitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input EB UncalibRecHit collection not found."; + return; + } + edm::Handle eeuncalibrechitHandle; + iEvent.getByToken(eeUncalibRecHitToken_, eeuncalibrechitHandle); + if (!eeuncalibrechitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input EE UncalibRecHit collection not found."; + return; + } + + edm::Handle ebrechitHandle; + iEvent.getByToken(ebRecHitToken_, ebrechitHandle); + if (!ebrechitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input EB RecHit collection not found."; + return; + } + edm::Handle eerechitHandle; + iEvent.getByToken(eeRecHitToken_, eerechitHandle); + if (!eerechitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input EE RecHit collection not found."; + return; + } + + edm::Handle pfrechitHandle; + iEvent.getByToken(PFRecHitToken_, pfrechitHandle); + if (!pfrechitHandle.isValid()) { + edm::LogPrint("RecHitTester") << "Input pfrechit collection not found."; + return; + } + + auto ebsimhits = *ebsimhitHandle; + auto eesimhits = *eesimhitHandle; + auto ebuncalibrechits = *ebuncalibrechitHandle; + auto eeuncalibrechits = *eeuncalibrechitHandle; + auto ebrechits = *ebrechitHandle; + auto eerechits = *eerechitHandle; + auto pfrechits = *pfrechitHandle; + + for (auto h : ebsimhits) { + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); + + h2d_ebsimHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_ebsimHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_ebsimHits_["Eta_Phi"]->Fill(eta, phi); + } + for (auto h : eesimhits) { + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); + + h2d_eesimHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_eesimHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_eesimHits_["Eta_Phi"]->Fill(eta, phi); + } + + for (auto h : ebuncalibrechits) { + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); + + h2d_ebuncalibRecHits_["En_Eta"]->Fill(h.amplitude(), eta); + h2d_ebuncalibRecHits_["En_Phi"]->Fill(h.amplitude(), phi); + h2d_ebuncalibRecHits_["Eta_Phi"]->Fill(eta, phi); + } + for (auto h : eeuncalibrechits) { + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); + + h2d_eeuncalibRecHits_["En_Eta"]->Fill(h.amplitude(), eta); + h2d_eeuncalibRecHits_["En_Phi"]->Fill(h.amplitude(), phi); + h2d_eeuncalibRecHits_["Eta_Phi"]->Fill(eta, phi); + } + + for (auto h : ebrechits) { + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); + + h2d_ebrecHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_ebrecHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_ebrecHits_["Eta_Phi"]->Fill(eta, phi); + } + for (auto h : eerechits) { + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); + + h2d_eerecHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_eerecHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_eerecHits_["Eta_Phi"]->Fill(eta, phi); + } + + for (auto h : pfrechits) { + float eta = caloGeom.getPosition(h.detId()).eta(); + float phi = caloGeom.getPosition(h.detId()).phi(); + + h2d_pfRecHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_pfRecHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_pfRecHits_["Eta_Phi"]->Fill(eta, phi); + } +} + +void RecHitTester::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("outFolder", "HLT/ParticleFlow"); + desc.add("ebSimHits", edm::InputTag("g4SimHits", "EcalHitsEB")); + desc.add("eeSimHits", edm::InputTag("g4SimHits", "EcalHitsEE")); + desc.add("ebUncalibRecHits", edm::InputTag("hltEcalUncalibRecHit", "EcalUncalibRecHitsEE")); + desc.add("eeUncalibRecHits", edm::InputTag("hltEcalUncalibRecHit", "EcalUncalibRecHitsEE")); + desc.add("ebRecHits", edm::InputTag("hltEcalRecHit", "EcalRecHitsEB")); + desc.add("eeRecHits", edm::InputTag("hltEcalRecHit", "EcalRecHitsEE")); + desc.add("pfRecHits", edm::InputTag("hltParticleFlowRecHitECALUnseeded")); + descriptions.addWithDefaultLabel(desc); +} + +DEFINE_FWK_MODULE(RecHitTester); diff --git a/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py new file mode 100644 index 0000000000000..9fe2d38200ebe --- /dev/null +++ b/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +import os +import argparse +import numpy as np +import utils +import matplotlib.pyplot as plt +plt.style.use('tableau-colorblind10') + +import ROOT +import mplhep as hep +hep.style.use("CMS") + +from makeHLTPFValidationPlots import plot2D, debug + +if __name__ == '__main__': + + def check_list_length(value): + if len(value) <= 1: + raise argparse.ArgumentTypeError("List must have more than one item") + return value + + class DependencyAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + if option_string == "--compare_files_labels" and not namespace.compare_files: + parser.error("`--compare_files_labels` requires `--compare_files` to be set") + if option_string == "--compare_files_labels" and not namespace.met: + parser.error("`--compare_files_labels` requires `--met` to be set") + if len(namespace.met) > 1: + parser.error("When comparing files only one MET collection can be used.") + if len(values) != len(namespace.compare_files): + parser.error("`--compare_files_labels` must have the same size as `--compare_files`") + setattr(namespace, self.dest, values) + + full_command = 'python3 Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py --odir -l TTbar -f ' + parser = argparse.ArgumentParser(description='Make HLT PF validation plots. \nExample command:\n' + full_command) + parser.add_argument('-o', '--odir', default="HLTPFValidationPlots", help='Path to the output directory.') + parser.add_argument('-l', '--sample_label', default="", help='Sample label for plotting.') + parser.add_argument('-e', '--era', default="Phase2", help="Chose between ['Phase2', 'Run3'].") + parser.add_argument('--EnFracCut', default=0.01, help='Cut on the sim cluster energy fraction.') + parser.add_argument('--PtCut', default=0.1, help='Cut on the sim cluster energy fraction.') + parser.add_argument('--digis', action='store_true', help='Plot digis, otherwise plot hits.') + + mutual_excl2 = parser.add_mutually_exclusive_group(required=True) + mutual_excl2.add_argument('-f', '--file', help='Paths to the DQM ROOT file.') + mutual_excl2.add_argument('-x', '--compare_files', nargs='+', type=check_list_length, + help='Compare the same collection in different DQM files.', ) + parser.add_argument('-y', '--compare_files_labels', nargs='+', + action=DependencyAction, help='Compare the same collection in different DQM files.',) + + args = parser.parse_args() + + utils.createDir(args.odir) + parentDir = os.path.dirname(args.odir) + if args.odir[-1] == '/': + parentDir = os.path.dirname(parentDir) + utils.createIndexPHP(src=parentDir, dest=args.odir) + + fontsize = 16 + colors = hep.style.CMS['axes.prop_cycle'].by_key()['color'] + markers = ('o', 's', 'd') + errorbar_kwargs = dict(capsize=3, elinewidth=0.8, capthick=2, linewidth=2, linestyle='') + + nSimClustersLabel = '# SimClusters' + nPFClustersLabel = '# PFClusters' + + titles = {'responsePt': r"$p_{T}^{Reco}/p_{T}^{Sim}$", + 'response': r"$E^{Reco}/E^{Sim}$", + 'av_responsePt': r"$$", + 'av_response': r"$$", + 'resolutionPt': r"$\sigma(p_{T}^{Reco}/p_{T}^{Sim}) / $", + 'resolution': r"$\sigma(E^{Reco}/E^{Sim}) / $", + 'eff': 'Efficiency', + 'fake': 'Fake Rate', + 'split': 'Split Rate', + 'merge': 'Merge Rate'} + + dqm_dir = f'DQMData/Run 1/HLT/Run summary/ParticleFlow/' + ('Digis' if args.digis else 'Hits') + afile = ROOT.TFile.Open(args.file) + utils.checkRootDir(afile, dqm_dir) + + debug('Start caching Hits histograms...') + subdirs = [] + cached_histos = {} + directory = afile.GetDirectory(dqm_dir) + for key in directory.GetListOfKeys(): + obj = key.ReadObj() + if isinstance(obj, ROOT.TDirectory): + subdirs.append(obj.GetName()) + subdir = afile.GetDirectory(f"{dqm_dir}/{obj.GetName()}") + for subkey in subdir.GetListOfKeys(): + name = subkey.GetName() + cached_histos[f"{obj.GetName()}/{name}"] = subkey.ReadObj() + else: + name = key.GetName() + cached_histos[name] = key.ReadObj() + debug(' ...done.') + + if args.digis: + vars2D = { + 'EcalEBDigisADC_Eta': dict(xtitle=r'$\max(ADC Counts)$', ytitle=r'$\eta$', var='# EB Digis'), + 'EcalEBDigisADC_Phi': dict(xtitle=r'$\max(ADC Counts)$', ytitle=r'$\phi$', var='# EB Digis'), + 'EcalEBDigisEta_Phi': dict(xtitle=r'$\eta$', ytitle=r'$\phi$', var='# EB Digis'), + 'EcalEEDigisADC_Eta': dict(xtitle=r'$\max(ADC Counts)$', ytitle=r'$\eta$', var='# EE Digis'), + 'EcalEEDigisADC_Phi': dict(xtitle=r'$\max(ADC Counts)$', ytitle=r'$\phi$', var='# EE Digis'), + 'EcalEEDigisEta_Phi': dict(xtitle=r'$\eta$', ytitle=r'$\phi$', var='# EE Digis'), + } + else: + vars2D = { + **{f'{det}UncalibRecHitsEn_Eta': dict(ytitle=r'$\eta$', var=f'# Uncalibrated {det} Reconstructed Hits', xtitle='Amplitude [ADC Counts]', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}UncalibRecHitsEn_Phi': dict(ytitle=r'$\phi$', var=f'# Uncalibrated {det} Reconstructed Hits', xtitle='Amplitude [ADC Counts]', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}UncalibRecHitsEta_Phi': dict(ytitle=r'$\phi$', var=f'# Uncalibrated {det} Reconstructed Hits', xtitle=r'$\eta$', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}UncalibRecHitsEta_Phi': dict(ytitle=r'$\phi$', var=f'# Uncalibrated {det} Reconstructed Hits', xtitle=r'$\eta$', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}RecHitsEn_Eta': dict(ytitle=r'$\eta$', var=f'# Reconstructed {det} Hits', xtitle='Energy [GeV]', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}RecHitsEn_Phi': dict(ytitle=r'$\phi$', var=f'# Reconstructed {det} Hits', xtitle='Energy [GeV]', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}RecHitsEta_Phi': dict(ytitle=r'$\phi$', var=f'# Reconstructed {det} Hits', xtitle=r'$\eta$', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}SimHitsEn_Eta': dict(ytitle=r'$\eta$', var=f'# Simulated {det} Hits', xtitle='Energy [GeV]', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}SimHitsEn_Phi': dict(ytitle=r'$\phi$', var=f'# Simulated {det} Hits', xtitle='Energy [GeV]', logz=True) + for det in ('EE', 'EB')}, + **{f'{det}SimHitsEta_Phi': dict(ytitle=r'$\phi$', var=f'# Simulated {det} Hits', xtitle=r'$\eta$', logz=True) + for det in ('EE', 'EB')}, + 'PFRecHitsEn_Eta': dict(ytitle=r'$\eta$', var='# PF Reconstructed Hits', xtitle='Energy [GeV]', logz=True), + 'PFRecHitsEn_Phi': dict(ytitle=r'$\phi$', var='# PF Reconstructed Hits', xtitle='Energy [GeV]', logz=True), + 'PFRecHitsEta_Phi': dict(ytitle=r'$\phi$', var='# PF Reconstructed Hits', xtitle=r'$\eta$', logz=True), + } + + for name, props in vars2D.items(): + root_hist = cached_histos[f"{name}"] + plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) From 38299e10ba024aaec785ea6ebbe43f212b44b4fc Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 27 Feb 2026 15:21:27 +0100 Subject: [PATCH 25/64] Add PF cluster timing. --- .../RecoParticleFlow/plugins/PFTester.cc | 85 +++++++++++-------- .../python/hltPFPostProcessor_cfi.py | 10 ++- .../scripts/makeHLTPFValidationPlots.py | 47 +++++++++- 3 files changed, 103 insertions(+), 39 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 3a942cbedc026..815d1ff747160 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -116,6 +116,7 @@ class PFTesterT : public DQMEDAnalyzer { {"Eta", std::make_tuple(50, -6.5, 6.5)}, {"Phi", std::make_tuple(50, -3.5, 3.5)}, {"Mult", std::make_tuple(200, 0., 200.)}, + {"Time", std::make_tuple(60, -60., 60.)}, }; const std::unordered_map> histoVarsSim = { {"En", std::make_tuple(100, 0., 100.)}, @@ -148,6 +149,10 @@ class PFTesterT : public DQMEDAnalyzer { {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, + {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, + {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, + {"Mult_Time", std::make_tuple(200, 0., 200., 200, -100., 100.)}, }; std::unordered_map> histo2dVarsSim = { @@ -840,14 +845,15 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { // fake and merge denominator h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); - // h_recoClusters_["Pt"]->Fill(recoClusters[recoId].pt()); - // h_recoClusters_["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); - - if (abs(recoClusters[recoId].eta()) > 1.35) { - std::cout << "============== RECO CLUSTER ANOMALY ========= " << std::endl; + if constexpr (std::is_same::value) { + h_recoClusters_["Time"]->Fill(recoClusters[recoId].time()); + } + + if (abs(recoClusters[recoId].eta()) > 2.) { + std::cout << "============== RECO CLUSTER ANOMALY ========= " << iEvent.eventAuxiliary().event() << std::endl; std::cout << recoClusters[recoId].eta() << std::endl; std::cout << recoClusters[recoId].energy() << std::endl; for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { @@ -861,11 +867,14 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); - // h2d_recoClusters_["Pt_Eta"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].eta()); - // h2d_recoClusters_["Pt_Phi"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].phi()); - // h2d_recoClusters_["Pt_Mult"]->Fill(recoClusters[recoId].pt(), recoClusters[recoId].size()); h2d_recoClusters_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClusters_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); + if constexpr (std::is_same::value) { + h2d_recoClusters_["En_Time"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].time()); + h2d_recoClusters_["Eta_Time"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].time()); + h2d_recoClusters_["Phi_Time"]->Fill(recoClusters[recoId].phi(), recoClusters[recoId].time()); + h2d_recoClusters_["Mult_Time"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].time()); + } const edm::Ref recoClusterRef(RecoCluster, recoId); const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); @@ -917,8 +926,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // fake numerator if (nSimMatchedToOneReco > 0) { h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - // h_recoClustersMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); - // h_recoClustersMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); @@ -929,25 +936,32 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e recoClusters[recoId].phi()); h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); - // h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Eta"]->Fill(recoClusters[recoId].pt(), - // recoClusters[recoId].eta()); - // h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Phi"]->Fill(recoClusters[recoId].pt(), - // recoClusters[recoId].phi()); - // h2d_recoClustersMatchedSimClusters_[ithr]["Pt_Mult"]->Fill(recoClusters[recoId].pt(), - // recoClusters[recoId].size()); h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - // merge numerator + if constexpr (std::is_same::value) { + h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoClusters[recoId].eta(), + recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoClusters[recoId].phi(), + recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoClusters[recoId].size(), + recoClusters[recoId].time()); + } + + // merge numerator if (nSimMatchedToOneReco > 1) { h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - // h_recoClustersMultiMatchedSimClusters_[ithr]["Pt"]->Fill(recoClusters[recoId].pt()); - // h_recoClustersMultiMatchedSimClusters_[ithr]["PtLow"]->Fill(recoClusters[recoId].pt()); h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); + if constexpr (std::is_same::value) { + h_recoClustersMultiMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); + } } } @@ -1013,21 +1027,21 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e passMatch = shared_energy_frac > thresh; } - std::cout << "===============================" << std::endl; - std::cout << "matchByScore? " << doMatchByScore_ << std::endl; - std::cout << "passMatch: " << passMatch << ", recoId: " << recoId << std::endl; - std::cout << "sim en: " << energySumSimHits << ", reco en: " << recoClusters[recoId].energy() << std::endl; - std::cout << "sim en frac: " << recoEnergyWeightedBySimFrac << std::endl; - std::cout << "sim eta: " << simClusters[simId].eta() << ", reco eta: " << recoClusters[recoId].eta() << ", sim track eta: " << simTrackEtaAtBoundary << std::endl; - std::cout << "sim phi: " << simClusters[simId].phi() << ", reco phi: " << recoClusters[recoId].phi() << std::endl; - std::cout << "score: " << recoPair.second.second << std::endl; - std::cout << "shared en frac: " << recoPair.second.first / energySumSimHits << std::endl; - std::cout << "n sim clusters: " << simClusters.size() << std::endl; - std::cout << "n matched reco clusters: " << simToRecoMatchedSorted.size() << std::endl; - for (const auto& recoPairDebug : simToRecoMatchedSorted) { - std::cout << "- score: " << recoPairDebug.second.second << ", share en frac: " << recoPairDebug.second.first / energySumSimHits << ", en: " << recoClusters[recoPairDebug.first.index()].energy() << std::endl; - } - std::cout << "threshold: " << thresh << std::endl; + // std::cout << "===============================" << std::endl; + // std::cout << "matchByScore? " << doMatchByScore_ << std::endl; + // std::cout << "passMatch: " << passMatch << ", recoId: " << recoId << std::endl; + // std::cout << "sim en: " << energySumSimHits << ", reco en: " << recoClusters[recoId].energy() << std::endl; + // std::cout << "sim en frac: " << recoEnergyWeightedBySimFrac << std::endl; + // std::cout << "sim eta: " << simClusters[simId].eta() << ", reco eta: " << recoClusters[recoId].eta() << ", sim track eta: " << simTrackEtaAtBoundary << std::endl; + // std::cout << "sim phi: " << simClusters[simId].phi() << ", reco phi: " << recoClusters[recoId].phi() << std::endl; + // std::cout << "score: " << recoPair.second.second << std::endl; + // std::cout << "shared en frac: " << recoPair.second.first / energySumSimHits << std::endl; + // std::cout << "n sim clusters: " << simClusters.size() << std::endl; + // std::cout << "n matched reco clusters: " << simToRecoMatchedSorted.size() << std::endl; + // for (const auto& recoPairDebug : simToRecoMatchedSorted) { + // std::cout << "- score: " << recoPairDebug.second.second << ", share en frac: " << recoPairDebug.second.first / energySumSimHits << ", en: " << recoClusters[recoPairDebug.first.index()].energy() << std::endl; + // } + // std::cout << "threshold: " << thresh << std::endl; for (auto hae : simClusters[simId].hits_and_energies()) { DetId idid(hae.first); if(idid.det() != DetId::Ecal) { @@ -1067,8 +1081,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].energy() / energySumSimHits); h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), - recoClusters[recoId].energy() / energySumSimHits); + h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].energy() / energySumSimHits); std::cout << "fill response: " << recoClusters[recoId].energy() / energySumSimHits << std::endl; std::cout << "============== break =================" << std::endl; break; diff --git a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py index 1c1f02124c51e..f7aad346a99be 100644 --- a/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFPostProcessor_cfi.py @@ -37,7 +37,11 @@ f"'Score{thr}/Fake_vs_PtPhi' 'Fake Rate vs p_{{T}}-#phi' Score{thr}/RecoClustersMatchedSimClustersPt_Phi RecoClustersPt_Phi fake", f"'Score{thr}/Fake_vs_PtMult' 'Fake Rate vs p_{{T}}-Mult' Score{thr}/RecoClustersMatchedSimClustersPt_Mult RecoClustersPt_Mult fake", f"'Score{thr}/Fake_vs_MultEta' 'Fake Rate vs Mult-#eta' Score{thr}/RecoClustersMatchedSimClustersMult_Eta RecoClustersMult_Eta fake", - f"'Score{thr}/Fake_vs_MultPhi' 'Fake Rate vs Mult-#phi' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClustersMult_Phi fake", + f"'Score{thr}/Fake_vs_MultPhi' 'Fake Rate vs Mult-#phi' Score{thr}/RecoClustersMatchedSimClustersMult_Phi RecoClustersMult_Phi fake", + f"'Score{thr}/Fake_vs_EnTime' 'Fake Rate vs Energy-Time' Score{thr}/RecoClustersMatchedSimClustersEn_Time RecoClustersEn_Time fake", + f"'Score{thr}/Fake_vs_EtaTime' 'Fake Rate vs #eta-Time' Score{thr}/RecoClustersMatchedSimClustersEta_Time RecoClustersEta_Time fake", + f"'Score{thr}/Fake_vs_PhiTime' 'Fake Rate vs #phi-Time' Score{thr}/RecoClustersMatchedSimClustersPhi_Time RecoClustersPhi_Time fake", + f"'Score{thr}/Fake_vs_MultTime' 'Fake Rate vs Mult-Time' Score{thr}/RecoClustersMatchedSimClustersMult_Time RecoClustersMult_Time fake", ) ], ), @@ -71,13 +75,15 @@ f"'Score{thr}/Fake_vs_Pt' 'Fake Rate vs p_{{T}}' Score{thr}/RecoClustersMatchedSimClustersPt RecoClustersPt fake", f"'Score{thr}/Fake_vs_Eta' 'Fake Rate vs #eta' Score{thr}/RecoClustersMatchedSimClustersEta RecoClustersEta fake", f"'Score{thr}/Fake_vs_Phi' 'Fake Rate vs #phi' Score{thr}/RecoClustersMatchedSimClustersPhi RecoClustersPhi fake", - f"'Score{thr}/Fake_vs_Mult' 'Fake Rate vs Multiplicity' Score{thr}/RecoClustersMatchedSimClustersMult RecoClustersMult fake", + f"'Score{thr}/Fake_vs_Mult' 'Fake Rate vs Multiplicity' Score{thr}/RecoClustersMatchedSimClustersMult RecoClustersMult fake", + f"'Score{thr}/Fake_vs_Time' 'Fake Rate vs Time' Score{thr}/RecoClustersMatchedSimClustersTime RecoClustersTime fake", # Merge rate f"'Score{thr}/Merge_vs_En' 'Merge Rate vs Energy' Score{thr}/RecoClustersMultiMatchedSimClustersEn RecoClustersEn", f"'Score{thr}/Merge_vs_Pt' 'Merge Rate vs p_{{T}}' Score{thr}/RecoClustersMultiMatchedSimClustersPt RecoClustersPt", f"'Score{thr}/Merge_vs_Eta' 'Merge Rate vs #eta' Score{thr}/RecoClustersMultiMatchedSimClustersEta RecoClustersEta", f"'Score{thr}/Merge_vs_Phi' 'Merge Rate vs #phi' Score{thr}/RecoClustersMultiMatchedSimClustersPhi RecoClustersPhi", f"'Score{thr}/Merge_vs_Mult' 'Merge Rate vs Mult' Score{thr}/RecoClustersMultiMatchedSimClustersMult RecoClustersMult", + f"'Score{thr}/Merge_vs_Time' 'Merge Rate vs Time' Score{thr}/RecoClustersMultiMatchedSimClustersTime RecoClustersTime", ) ], ), diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 8a936a74568ee..87c5d70bcccac 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -703,7 +703,13 @@ def __call__(self, parser, namespace, values, option_string=None): num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, ), - # Cluster merge rate (WIP) + f'{subdir}/Fake_vs_Time': InputArgs( + ratio=f'{subdir}/Fake_vs_Time', + den=f'RecoClustersTime', legden='RecoClusters', + num=f'{subdir}/RecoClustersMatchedSimClustersTime', legnum='Matched RecoClusters', + xtitle='Time [ns]', ytitle=nPFClustersLabel, rebin=4, logy=True, + ), + # Cluster merge rate f'{subdir}/Merge_vs_En': InputArgs( ratio=f'{subdir}/Merge_vs_En', den=f'RecoClustersEn', legden='RecoClusters', @@ -733,6 +739,12 @@ def __call__(self, parser, namespace, values, option_string=None): num=f'{subdir}/RecoClustersMultiMatchedSimClustersMult', legnum='Multi Matched RecoClusters', xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4 ), + f'{subdir}/Merge_vs_Time': InputArgs( + ratio=f'{subdir}/Merge_vs_Time', + den=f'RecoClustersTime', legden='RecoClusters', + num=f'{subdir}/RecoClustersMultiMatchedSimClustersTime', legnum='Multi Matched RecoClusters', + xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4 + ), } # Compare pairs of variables @@ -770,6 +782,7 @@ def __call__(self, parser, namespace, values, option_string=None): "Fake_vs_Eta" : InputArgs(ytitle=titles['fake'], xtitle=r'$\eta$'), "Fake_vs_Phi" : InputArgs(ytitle=titles['fake'], xtitle=r'$\phi$'), "Fake_vs_Mult" : InputArgs(ytitle=titles['fake'], rebin=4, xtitle='Multiplicity'), + "Fake_vs_Time" : InputArgs(ytitle=titles['fake'], rebin=4, xtitle='Time [ns]'), "Merge_vs_En" : InputArgs(ytitle=titles['merge'], rebin=4, xtitle='Energy [GeV]'), "Merge_vs_Pt" : InputArgs(ytitle=titles['merge'], rebin=4, xtitle='$p_{T} [GeV]$'), "Merge_vs_Eta" : InputArgs(ytitle=titles['merge'], xtitle=r'$\eta$'), @@ -890,6 +903,7 @@ def __call__(self, parser, namespace, values, option_string=None): plotOverlay(subdirs, cached_histos, fitstr.format('Width'), args.match_by_score, args.sample_label, args.era, props, outdir=args.odir) vars2D = { + # Clusters 'SimClustersEnFrac_Mult': dict(ytitle='Multiplicity', var='# Sim Clusters', xtitle='Energy Fraction'), 'SimClustersEn_Mult': dict(ytitle='Multiplicity', var='# Sim Clusters', xtitle='Energy [GeV]'), 'SimClustersEn_Eta': dict(ytitle=r'$\eta$', var='# Sim Clusters', xtitle='Energy [GeV]'), @@ -899,6 +913,37 @@ def __call__(self, parser, namespace, values, option_string=None): 'RecoClustersEn_Eta': dict(ytitle=r'$\eta$', var='# Reco Clusters', xtitle='Energy [GeV]', logz=True), 'RecoClustersEn_Phi': dict(ytitle=r'$\phi$', var='# Reco Clusters', xtitle='Energy [GeV]', logz=True), 'RecoClustersMult_Eta': dict(xtitle='Multiplicity', var='# Reco Clusters', ytitle=r'$\eta$'), + 'RecoClustersEn_Time': dict(xtitle='Energy [GeV]', var='# Reco Clusters', ytitle='Time [ns]', logz=True), + 'RecoClustersEta_Time': dict(xtitle=r'$\eta$', var='# Reco Clusters', ytitle='Time [ns]', logz=True), + 'RecoClustersPhi_Time': dict(xtitle=r'$\phi$', var='# Reco Clusters', ytitle='Time [ns]', logz=True), + 'RecoClustersMult_Time': dict(xtitle=r'Multiplicity', var='# Reco Clusters', ytitle='Time [ns]', logz=True), + # Hits in Clusters + 'RecoHitsInClustersEn_Eta': dict(xtitle='Energy [GeV]', ytitle=r'$\eta$', var='# Reco Hits in Clusters'), + 'RecoHitsInClustersEn_Phi': dict(xtitle='Energy [GeV]', ytitle=r'$\phi$', var='# Reco Hits in Clusters'), + 'RecoHitsInClustersEta_Phi': dict(xtitle=r'$\eta$', ytitle=r'$\phi$', var='# Reco Hits in Clusters'), + 'RecoHitsInClustersEn_Time': dict(xtitle='Energy [GeV]', ytitle=r'Time [ns]', var='# Reco Hits in Clusters'), + 'RecoHitsInClustersEta_Time': dict(xtitle=r'$\eta$', ytitle=r'Time [ns]', var='# Reco Hits in Clusters'), + 'RecoHitsInClustersPhi_Time': dict(xtitle=r'$\phi$', ytitle=r'Time [ns]', var='# Reco Hits in Clusters'), + # Matched + **{f'{subdir}/RecoClustersMatchedSimClustersEn_Eta': dict( + xtitle='Energy [GeV]', var='# Matched Reco Clusters', ytitle=r'$\eta$', logz=True + ) for subdir in subdirs}, + **{f'{subdir}/RecoClustersMatchedSimClustersEn_Phi': dict( + xtitle='Energy [GeV]', var='# Matched Reco Clusters', ytitle=r'$\phi$', logz=True + ) for subdir in subdirs}, + **{f'{subdir}/RecoClustersMatchedSimClustersEn_Time': dict( + xtitle='Energy [GeV]', var='# Matched Reco Clusters', ytitle='Time [ns]', logz=True + ) for subdir in subdirs}, + **{f'{subdir}/RecoClustersMatchedSimClustersEta_Time': dict( + xtitle=r'$\eta$', var='# Matched Reco Clusters', ytitle='Time [ns]', logz=True + ) for subdir in subdirs}, + **{f'{subdir}/RecoClustersMatchedSimClustersPhi_Time': dict( + xtitle=r'$\phi$', var='# Matched Reco Clusters', ytitle='Time [ns]', logz=True + ) for subdir in subdirs}, + **{f'{subdir}/RecoClustersMatchedSimClustersMult_Time': dict( + xtitle='Multiplicity', var='# Matched Reco Clusters', ytitle='Time [ns]', logz=True + ) for subdir in subdirs}, + # Score 'simToRecoScore_En': dict(ytitle='SimCluster Energy [GeV]', var='# Clusters', xtitle='SimToReco Score'), 'simToRecoScore_EnFrac': dict(ytitle='Energy Fraction', var='# Clusters', xtitle='SimToReco Score'), 'simToRecoScore_EnSimTrack': dict(ytitle='SimTrack Energy [GeV]', var='# Clusters', xtitle='SimToReco Score'), From 72ef2e74eecfbe5d1730e17768610aed77e96841 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 20 Feb 2026 15:06:19 +0100 Subject: [PATCH 26/64] Add info on hits within the clusters. --- .../RecoParticleFlow/plugins/PFTester.cc | 209 ++++++++---------- 1 file changed, 98 insertions(+), 111 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 815d1ff747160..416470edf50dd 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -140,36 +140,47 @@ class PFTesterT : public DQMEDAnalyzer { std::vector h_nSimMatchedToOneReco_; std::vector h_nRecoMatchedToOneSim_; - std::unordered_map> histo2dVarsReco = { - {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, - {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, - {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, - {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, - {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, - {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, - {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, - {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, - {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, + std::unordered_map> h2dVarsRecoHits = { + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, + {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, + {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, + }; + + std::unordered_map> h2dVarsRecoClusters = { + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, + {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, + {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, + {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, + {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, + {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, + {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, {"Mult_Time", std::make_tuple(200, 0., 200., 200, -100., 100.)}, }; - std::unordered_map> histo2dVarsSim = { - {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, - {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"EnFrac_Eta", std::make_tuple(220, 0., 1.1, 50, -6.5, 6.5)}, - {"EnFrac_Phi", std::make_tuple(220, 0., 1.1, 50, -3.5, 3.5)}, - {"EnFrac_Mult", std::make_tuple(220, 0., 1.1, 200, 0., 200.)}, - {"EnSimTrack_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"EnSimTrack_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + std::unordered_map> h2dVarsSim = { + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + {"EnFrac_Eta", std::make_tuple(220, 0., 1.1, 50, -6.5, 6.5)}, + {"EnFrac_Phi", std::make_tuple(220, 0., 1.1, 50, -3.5, 3.5)}, + {"EnFrac_Mult", std::make_tuple(220, 0., 1.1, 200, 0., 200.)}, + {"EnSimTrack_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"EnSimTrack_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, {"EnSimTrack_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, - {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, - {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, - {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, - {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, + {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, + {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, + {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, + {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, }; using U2Map = std::unordered_map; @@ -178,6 +189,8 @@ class PFTesterT : public DQMEDAnalyzer { VU2Map h2d_simClustersMatchedRecoClusters_; U2Map h2d_recoClusters_; VU2Map h2d_recoClustersMatchedSimClusters_; + + U2Map h2d_recoHitsInClusters_; VU2Map h2d_responsePt_; VU2Map h2d_responseE_; @@ -355,7 +368,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, ibook.book1D("RecoClusters" + hVar.first, "RecoClusters;" + hVar.first, nBins, hMin, hMax); } - for (auto& h2dVar : histo2dVarsSim) { + for (auto& h2dVar : h2dVarsSim) { auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); @@ -368,12 +381,12 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMinY, hMaxY); } - for (auto& h2dVar : histo2dVarsReco) { + for (auto& h2dVar : h2dVarsRecoClusters) { auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_recoClusters_[h2dVar.first] = ibook.book2D("RecoClusters" + h2dVar.first, - "RecoClusters;" + x_title + ";" + y_title, + "RecoClusters;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, @@ -381,6 +394,19 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMinY, hMaxY); } + for (auto& h2dVar : h2dVarsRecoHits) { + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); + h2d_recoHitsInClusters_[h2dVar.first] = ibook.book2D("RecoHitsInClusters" + h2dVar.first, + "RecoHitsInClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + } for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); @@ -431,7 +457,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMin, hMax); } - for (auto& h2dVar : histo2dVarsSim) { + for (auto& h2dVar : h2dVarsSim) { auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); @@ -445,7 +471,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMinY, hMaxY); } - for (auto& h2dVar : histo2dVarsReco) { + for (auto& h2dVar : h2dVarsRecoClusters) { auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); @@ -515,8 +541,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- - std::cout << std::endl; - std::cout << "--- Analyze --- " << std::endl; edm::Handle Rechit; iEvent.getByToken(RechitToken_, Rechit); if (!Rechit.isValid()) { @@ -611,7 +635,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e edm::LogPrint("PFTester") << " caloParticle [" << cpId << "]: energy=" << caloParticles[cpId].energy() << ", energySumSimClusters=" << energySumSimClusters << ", energySumSimHits=" << energySumSimHits - << ", recoEnergySumWeightedBySimFrac=" << recoEnergySumWeightedBySimFrac << std::endl; + << ", recoEnergySumWeightedBySimFrac=" << recoEnergySumWeightedBySimFrac; #endif h_CPToSCEnergyFraction_->Fill(energySumSimClusters / caloParticles[cpId].energy()); @@ -631,7 +655,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e edm::LogPrint("PFTester") << " caloParticle [" << cpId << "] matched to RecoCluster [" << recoPair.first.index() << "] with shared energy: " << recoPair.second.first << ", shared energy fraction: " << recoPair.second.first / recoEnergySumWeightedBySimFrac - << ", score: " << recoPair.second.second << std::endl; + << ", score: " << recoPair.second.second; #endif h_CP_simToRecoScore_->Fill(recoPair.second.second); h_CP_simToRecoShEnF_->Fill(recoPair.second.first / recoEnergySumWeightedBySimFrac); @@ -696,6 +720,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_simClusters_["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); h2d_simClusters_["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClusters_["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); h2d_simClusters_["En_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); h2d_simClusters_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); h2d_simClusters_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); @@ -728,7 +753,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e auto ev = simClusters[simId].g4Tracks()[0].eventId().event(); auto bx = simClusters[simId].g4Tracks()[0].eventId().bunchCrossing(); edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx - << ", en=" << energySumSimHits << ", hits="; + << ", en=" << energySumSimHits; const auto& hits_fractions = simClusters[simId].hits_and_fractions(); const auto& hits_energies = simClusters[simId].hits_and_energies(); @@ -744,7 +769,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e << "], en=" << recoClusters[recoPair.first.index()].energy() << ", with shared energy: " << recoPair.second.first << ", shared energy fraction: " << recoPair.second.first / recoEnergySumWeightedBySimFrac - << ", score: " << recoPair.second.second << ", hits="; + << ", score: " << recoPair.second.second; for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { DetId id(hit_energy.recHitRef()->detId()); const GlobalPoint pos = caloGeom.getPosition(id); @@ -796,6 +821,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, @@ -843,6 +869,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { + // fake and merge denominator h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); @@ -852,20 +879,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h_recoClusters_["Time"]->Fill(recoClusters[recoId].time()); } - if (abs(recoClusters[recoId].eta()) > 2.) { - std::cout << "============== RECO CLUSTER ANOMALY ========= " << iEvent.eventAuxiliary().event() << std::endl; - std::cout << recoClusters[recoId].eta() << std::endl; - std::cout << recoClusters[recoId].energy() << std::endl; - for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { - DetId idid = haf.first; - if(idid.det() != DetId::Ecal or idid.subdetId() != EcalBarrel) { - std::cout << idid.det() << " < " << idid.subdetId() << std::endl; - } - } - std::cout << "============================================= " << std::endl; - } h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); + h2d_recoClusters_["Eta_Phi"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].phi()); h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); h2d_recoClusters_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClusters_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); @@ -876,6 +892,27 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_recoClusters_["Mult_Time"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].time()); } + // hits belonging to the clusters + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { + DetId id = haf.first; + const GlobalPoint pos = caloGeom.getPosition(id); + + auto rechitIt = + std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + if (rechitIt == pfRechit.end()) { + edm::LogPrint("PFTester") << " PF Hit not found! | eta=" << pos.eta() << ", phi=" << pos.phi(); + } else { + h2d_recoHitsInClusters_["En_Eta"]->Fill(rechitIt->energy(), pos.eta()); + h2d_recoHitsInClusters_["En_Phi"]->Fill(rechitIt->energy(), pos.phi()); + h2d_recoHitsInClusters_["Eta_Phi"]->Fill(pos.eta(), pos.phi()); + h2d_recoHitsInClusters_["En_Time"]->Fill(rechitIt->energy(), rechitIt->time()); + h2d_recoHitsInClusters_["Eta_Time"]->Fill(pos.eta(), rechitIt->time()); + h2d_recoHitsInClusters_["Phi_Time"]->Fill(pos.phi(), rechitIt->time()); + } + } + + // matching const edm::Ref recoClusterRef(RecoCluster, recoId); const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); if (recoToSimIt == recoToSimAssoc.end()) @@ -894,8 +931,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e #ifdef debug edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId - << " : matched simCluster id = " << simPairIdx << " score = " << simPair.second - << std::endl; + << " : matched simCluster id = " << simPairIdx << " score = " << simPair.second; #endif double energySumSimHits = simClusterEnergy(simClusters[simPairIdx]); @@ -934,13 +970,14 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e recoClusters[recoId].eta()); h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoClusters[recoId].eta(), + recoClusters[recoId].phi()); h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - if constexpr (std::is_same::value) { h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoClusters[recoId].energy(), @@ -972,8 +1009,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ----- Cluster response computation --------------------------------- // -------------------------------------------------------------------- - std::cout << std::endl; - std::cout << "--- Event " << iEvent.eventAuxiliary().event() << " ---" << std::endl; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { double energySumSimHits = simClusterEnergy(simClusters[simId]); @@ -1026,66 +1061,18 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e double shared_energy_frac = shared_energy / recoEnergyWeightedBySimFrac; passMatch = shared_energy_frac > thresh; } - - // std::cout << "===============================" << std::endl; - // std::cout << "matchByScore? " << doMatchByScore_ << std::endl; - // std::cout << "passMatch: " << passMatch << ", recoId: " << recoId << std::endl; - // std::cout << "sim en: " << energySumSimHits << ", reco en: " << recoClusters[recoId].energy() << std::endl; - // std::cout << "sim en frac: " << recoEnergyWeightedBySimFrac << std::endl; - // std::cout << "sim eta: " << simClusters[simId].eta() << ", reco eta: " << recoClusters[recoId].eta() << ", sim track eta: " << simTrackEtaAtBoundary << std::endl; - // std::cout << "sim phi: " << simClusters[simId].phi() << ", reco phi: " << recoClusters[recoId].phi() << std::endl; - // std::cout << "score: " << recoPair.second.second << std::endl; - // std::cout << "shared en frac: " << recoPair.second.first / energySumSimHits << std::endl; - // std::cout << "n sim clusters: " << simClusters.size() << std::endl; - // std::cout << "n matched reco clusters: " << simToRecoMatchedSorted.size() << std::endl; - // for (const auto& recoPairDebug : simToRecoMatchedSorted) { - // std::cout << "- score: " << recoPairDebug.second.second << ", share en frac: " << recoPairDebug.second.first / energySumSimHits << ", en: " << recoClusters[recoPairDebug.first.index()].energy() << std::endl; - // } - // std::cout << "threshold: " << thresh << std::endl; - for (auto hae : simClusters[simId].hits_and_energies()) { - DetId idid(hae.first); - if(idid.det() != DetId::Ecal) { - std::cout << "sc ERROR 1!!!!!!!!!!!!" << std::endl; - } - if(idid.subdetId() != EcalBarrel) { - std::cout << "sc ERROR 2!!!!!!!!!!!!" << std::endl; - } - } - for (auto pfrh : pfRechit) { - DetId idid(pfrh.detId()); - if(idid.det() != DetId::Ecal) { - std::cout << "pfhit ERROR 1!!!!!!!!!!!!" << std::endl; - } - if(idid.subdetId() != EcalBarrel) { - std::cout << "pfhit ERROR 2!!!!!!!!!!!!" << std::endl; - } - } - for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { - DetId idid = haf.first; - if(idid.det() != DetId::Ecal) { - std::cout << "rc ERROR 1!!!!!!!!!!!!" << std::endl; - } - if(idid.subdetId() != EcalBarrel) { - std::cout << "rc ERROR 2!!!!!!!!!!!!" << std::endl; - } - } - std::cout << "===============================" << std::endl; if (passMatch) { - h2d_responseE_[ithr]["En"]->Fill(energySumSimHits, - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), - recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), recoClusters[recoId].energy() / energySumSimHits); - h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), recoClusters[recoId].energy() / energySumSimHits); - std::cout << "fill response: " << recoClusters[recoId].energy() / energySumSimHits << std::endl; - std::cout << "============== break =================" << std::endl; + float resp = recoClusters[recoId].energy() / energySumSimHits; + h2d_responseE_[ithr]["En"]->Fill(energySumSimHits, resp); + h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, resp); + h2d_responseE_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), resp); + h2d_responseE_[ithr]["Pt"]->Fill(simClusters[simId].pt(), resp); + h2d_responseE_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary, resp); + h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), resp); + h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), resp); break; - } + } } } } From 012c130fe989dbc8cbf90ccf54cab0ce8b459128 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Wed, 25 Feb 2026 15:30:19 +0100 Subject: [PATCH 27/64] Add digi and rechit tester to PF validation chain. --- .../python/hltPFValidation_cfi.py | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index e51f344bfb48d..c5cc4965b2c60 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -39,12 +39,33 @@ ptCut = cms.double(0.), etaCut = cms.double(3.0) ) - from Configuration.Eras.Modifier_phase2_common_cff import phase2_common phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp"), etaCut = cms.double(1.48)) +hltDigisTesterECAL = cms.EDProducer("DigisTester", + ecalEBDigis = cms.InputTag("hltEcalDigis", "ebDigis"), + ecalEEDigis = cms.InputTag("hltEcalDigis", "eeDigis"), + outFolder = cms.string('HLT/ParticleFlow'), +) + +hltRecHitTesterECAL = cms.EDProducer("RecHitTester", + outFolder = cms.string('HLT/ParticleFlow'), + ebSimHits = cms.InputTag('g4SimHits', 'EcalHitsEB'), + eeSimHits = cms.InputTag('g4SimHits', 'EcalHitsEE'), + ebRecHits = cms.InputTag('hltEcalRecHit', 'EcalRecHitsEB'), + eeRecHits = cms.InputTag('hltEcalRecHit', 'EcalRecHitsEE'), + ebUncalibRecHits = cms.InputTag('hltEcalUncalibRecHit', 'EcalUncalibRecHitsEB'), + eeUncalibRecHits = cms.InputTag('hltEcalUncalibRecHit', 'EcalUncalibRecHitsEE'), + pfRecHits = cms.InputTag('hltParticleFlowRecHitECALUnseeded'), +) +phase2_common.toModify(hltRecHitTesterECAL, + eeSimHits = cms.InputTag('g4SimHits', 'HGCHitsEE'), + ebUncalibRecHits = cms.InputTag('hltEcalMultiFitUncalibRecHit', 'EcalUncalibRecHitsEB'), + eeUncalibRecHits = cms.InputTag('hltEcalMultiFitUncalibRecHit', 'EcalUncalibRecHitsEE') + ) + hltPFClusterTesterECALWithCut = hltPFClusterTesterECAL.clone( - enFracCut = cms.double(0.01), + enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) @@ -54,7 +75,7 @@ ) hltPFClusterTesterECALShEnFWithCut = hltPFClusterTesterECALShEnF.clone( - enFracCut = cms.double(0.01), + enFracCut = cms.double(0.01), ptCut = cms.double(0.1) ) @@ -65,4 +86,6 @@ +hltPFClusterCaloParticleAssociationProducerECAL +hltPFClusterTesterECALWithCut +hltPFClusterTesterECALShEnFWithCut + +hltDigisTesterECAL + +hltRecHitTesterECAL ) From 84c496b9286a8b382c1744edbb7774e85e95b7ef Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 3 Mar 2026 10:04:46 +0100 Subject: [PATCH 28/64] Use less error-prone logz scale for PFRecHits. --- .../RecoParticleFlow/scripts/makeHLTHitValidationPlots.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py index 9fe2d38200ebe..b17268dd6d6c4 100644 --- a/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTHitValidationPlots.py @@ -127,9 +127,9 @@ def __call__(self, parser, namespace, values, option_string=None): for det in ('EE', 'EB')}, **{f'{det}SimHitsEta_Phi': dict(ytitle=r'$\phi$', var=f'# Simulated {det} Hits', xtitle=r'$\eta$', logz=True) for det in ('EE', 'EB')}, - 'PFRecHitsEn_Eta': dict(ytitle=r'$\eta$', var='# PF Reconstructed Hits', xtitle='Energy [GeV]', logz=True), - 'PFRecHitsEn_Phi': dict(ytitle=r'$\phi$', var='# PF Reconstructed Hits', xtitle='Energy [GeV]', logz=True), - 'PFRecHitsEta_Phi': dict(ytitle=r'$\phi$', var='# PF Reconstructed Hits', xtitle=r'$\eta$', logz=True), + 'PFRecHitsEn_Eta': dict(ytitle=r'$\eta$', var='# PF Reconstructed Hits', xtitle='Energy [GeV]', logz=False), + 'PFRecHitsEn_Phi': dict(ytitle=r'$\phi$', var='# PF Reconstructed Hits', xtitle='Energy [GeV]', logz=False), + 'PFRecHitsEta_Phi': dict(ytitle=r'$\phi$', var='# PF Reconstructed Hits', xtitle=r'$\eta$', logz=False), } for name, props in vars2D.items(): From c08a5382fa12a061692b608e2fcdc0537978a83d Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 3 Mar 2026 10:27:51 +0100 Subject: [PATCH 29/64] Support era and plot label. --- .../scripts/showECALcrystals.py | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Validation/RecoParticleFlow/scripts/showECALcrystals.py b/Validation/RecoParticleFlow/scripts/showECALcrystals.py index bc016e1cd71f4..60b8f96270c2c 100755 --- a/Validation/RecoParticleFlow/scripts/showECALcrystals.py +++ b/Validation/RecoParticleFlow/scripts/showECALcrystals.py @@ -52,7 +52,8 @@ def add_black(cmap): new_colors = np.vstack((colors, [0, 0, 0, 1])) # add black return mcolors.ListedColormap(new_colors) -def plotEvent(geom, hits, clusters, out, zoom, var='energy', zlabel='', categorical=False): +def plotEvent(geom, hits, clusters, out, zoom, + era='Phase2', label='', var='energy', zlabel='', categorical=False): """ Plot single event on top of the geometry. """ @@ -66,8 +67,10 @@ def plotEvent(geom, hits, clusters, out, zoom, var='energy', zlabel='', categori fig, ax = plt.subplots() params = dict(ax=ax, fontsize=15) - hep.cms.text(' Phase-2 Simulation Preliminary', **params) - hep.cms.lumitext("CloseByElectron | 14 TeV", **params) + if era == 'Phase2': era='Phase-2'; en='14' + elif era == 'Run3': era='Run-3'; en='13.6' + hep.cms.text(f' {era} Simulation Preliminary', **params) + hep.cms.lumitext(label + f' | {en} TeV', **params) if categorical: cmap = add_black(plt.get_cmap('tab10')) @@ -179,7 +182,7 @@ def showECAL(infile, outfile, props): plotEvent(dfGeom, dfHits, dfClusters, out=os.path.join(outfile, outname + '_allhits'), - var='energy', zoom=props.zoom, + era=props.era, label=props.label, var='energy', zoom=props.zoom, zlabel=('PF' if prefix=='Reco' else 'Sim') + ' RecHit Energy [GeV]') plotEvent(dfGeom, dfHitsInClusters, dfClusters, out=os.path.join(outfile, outname + '_clhits'), @@ -193,17 +196,28 @@ def showECAL(infile, outfile, props): class InputArgs: zoom: bool nevents: int + era: str + label: str if __name__ == '__main__': full_command = 'python3 Validation/RecoParticleFlow/scripts/showECALcrystals.py --file ' parser = argparse.ArgumentParser(description='Show position of crystals. \nExample command:\n' + full_command) - parser.add_argument('-i', '--file', help='Path to the input ROOT file.') - parser.add_argument('-o', '--outdir', help='Path to the output folder where the events will be stored.') - parser.add_argument('-z', '--zoom', help='Zoom over a hard-coded eta/phi region: (min_eta, max_eta, min_phi, max_phi)', nargs=4, type=float, default=None) - parser.add_argument('-n', '--nevents', help='Number of events to plot. If the input file has less events, then it plots all events of the input file.', default=10) + parser.add_argument('-i', '--file', + help='Path to the input ROOT file.') + parser.add_argument('-o', '--outdir', + help='Path to the output folder where the events will be stored.') + parser.add_argument('-z', '--zoom', + help='Zoom over a hard-coded eta/phi region: (min_eta, max_eta, min_phi, max_phi)', nargs=4, type=float, default=None) + parser.add_argument('-n', '--nevents', + help='Number of events to plot. If the input file has less events, then it plots all events of the input file.', default=10) + parser.add_argument('-e', '--era', required=True, + help="Chose between ['Phase2', 'Run3'].") + parser.add_argument('-l', '--sample_label', required=True, + help='Sample label for plotting.') args = parser.parse_args() - props = InputArgs(zoom=args.zoom, nevents=args.nevents) + props = InputArgs(zoom=args.zoom, nevents=args.nevents, + era=args.era, label=args.sample_label) showECAL(args.file, args.outdir, props) From 82eed0fb6ae6d66bd5ebf1e75a5a8ba0754e493b Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 3 Mar 2026 14:53:38 +0100 Subject: [PATCH 30/64] Remove collections as they are already present in FEVTDEBUGHLTEventContent. --- Configuration/EventContent/python/EventContent_cff.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Configuration/EventContent/python/EventContent_cff.py b/Configuration/EventContent/python/EventContent_cff.py index a5be0e573c1d4..3660d17f802d8 100644 --- a/Configuration/EventContent/python/EventContent_cff.py +++ b/Configuration/EventContent/python/EventContent_cff.py @@ -695,14 +695,6 @@ def SwapKeepAndDrop(l): FEVTDEBUGHLTEventContent.outputCommands.append('keep *_*_MergedTrackTruth_*') FEVTDEBUGHLTEventContent.outputCommands.append('keep *_*_StripDigiSimLink_*') FEVTDEBUGHLTEventContent.outputCommands.append('keep *_*_PixelDigiSimLink_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowRecHitECALUnseeded_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowRecHitHBHE_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowClusterECALUnseeded_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowClusterHCAL_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowClusterHF_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlow_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltParticleFlowBlock_*_*') -FEVTDEBUGHLTEventContent.outputCommands.append('keep *_hltLightPFTracks_*_*') from Configuration.ProcessModifiers.hltClusterSplitting_cff import hltClusterSplitting hltClusterSplitting.toModify(FEVTDEBUGHLTEventContent, From ba990a474ab1bf68de8d89939b134c487ad3c1f4 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 3 Mar 2026 15:20:53 +0100 Subject: [PATCH 31/64] Code checks and format. --- .../HGCalRecAlgos/src/RecHitTools.cc | 5 +- .../plugins/RecHitMapProducer.cc | 25 +- .../RecoParticleFlow/plugins/DigisTester.cc | 106 +++--- .../plugins/EcalGeometryAnalyzer.cc | 323 ++++++++-------- .../RecoParticleFlow/plugins/PFTester.cc | 349 +++++++++--------- .../RecoParticleFlow/plugins/RecHitTester.cc | 169 ++++----- 6 files changed, 494 insertions(+), 483 deletions(-) diff --git a/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc b/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc index 43eb13cc412a4..59d2b70bcc8c4 100644 --- a/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc +++ b/RecoLocalCalo/HGCalRecAlgos/src/RecHitTools.cc @@ -113,7 +113,8 @@ void RecHitTools::setGeometry(const CaloGeometry& geom) { bhFirstLayer_ = bhOffset_ + (geomBH->topology().dddConstants()).firstLayer(); bhLastLayer_ = bhOffset_ + (geomBH->topology().dddConstants()).lastLayer(true); bhMaxIphi_ = geomBH->topology().dddConstants().maxCells(true); - } else if (static_cast(geom_->getSubdetectorGeometry(DetId::Forward, ForwardSubdetector::HGCEE))) { + } else if (static_cast( + geom_->getSubdetectorGeometry(DetId::Forward, ForwardSubdetector::HGCEE))) { geometryType_ = 0; geomEE = static_cast(geom_->getSubdetectorGeometry(DetId::Forward, ForwardSubdetector::HGCEE)); @@ -526,11 +527,13 @@ bool RecHitTools::isScintillatorFine(const DetId& id) const { return false; } } + bool RecHitTools::isBarrel(const DetId& id) const { return (id.det() == DetId::Ecal && id.subdetId() == EcalBarrel) || (id.det() == DetId::Hcal && id.subdetId() == HcalBarrel) || (id.det() == DetId::Hcal && id.subdetId() == HcalOuter); } + bool RecHitTools::isOnlySilicon(const unsigned int layer) const { // HFnose TODO bool isonlysilicon = (layer % bhLastLayer_) < bhOffset_; diff --git a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc index 02539669e3b3c..152d5a029d210 100644 --- a/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc +++ b/RecoLocalCalo/HGCalRecProducers/plugins/RecHitMapProducer.cc @@ -43,10 +43,10 @@ RecHitMapProducer::RecHitMapProducer(const edm::ParameterSet& ps) std::vector tags = ps.getParameter>("hits"); for (auto& tag : tags) { if (tag.label().find("HGCalRecHit") != std::string::npos) { - hgcal_hits_tag_.push_back(tag); + hgcal_hits_tag_.push_back(tag); hgcal_hits_token_.push_back(consumes(tag)); } else { - pf_hits_tag_.push_back(tag); + pf_hits_tag_.push_back(tag); pf_hits_token_.push_back(consumes(tag)); } } @@ -68,7 +68,6 @@ void RecHitMapProducer::fillDescriptions(edm::ConfigurationDescriptions& descrip } void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::EventSetup& es) const { - if (doHgcalHits_) { auto hitMapHGCal = std::make_unique(); @@ -80,11 +79,13 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event // Check validity of all handles if (!ee_hits.isValid() || !fh_hits.isValid() || !bh_hits.isValid()) { - edm::LogWarning("HGCalRecHitMapProducer") << "One or more of the following HGCal hit collections are unavailable: "; - for (auto& tag : hgcal_hits_tag_) { - edm::LogWarning("HGCalRecHitMapProducer") << " - " << tag; - } - edm::LogWarning("HGCalRecHitMapProducer") << "Returning an empty map and an empty RefProdVectorHGCRecHitCollection"; + edm::LogWarning("HGCalRecHitMapProducer") + << "One or more of the following HGCal hit collections are unavailable: "; + for (auto& tag : hgcal_hits_tag_) { + edm::LogWarning("HGCalRecHitMapProducer") << " - " << tag; + } + edm::LogWarning("HGCalRecHitMapProducer") + << "Returning an empty map and an empty RefProdVectorHGCRecHitCollection"; evt.put(std::make_unique>(), "RefProdVectorHGCRecHitCollection"); evt.put(std::move(hitMapHGCal), "hgcalRecHitMap"); } else { @@ -117,10 +118,10 @@ void RecHitMapProducer::produce(edm::StreamID, edm::Event& evt, const edm::Event if (!ecal_hits.isValid() || !hbhe_hits.isValid()) { edm::LogWarning("HGCalRecHitMapProducer") << "One or more of the following PF hit collections are unavailable: "; - for (auto& tag : pf_hits_tag_) { - edm::LogWarning("HGCalRecHitMapProducer") << " - " << tag; - } - edm::LogWarning("HGCalRecHitMapProducer") << "Returning an empty map."; + for (auto& tag : pf_hits_tag_) { + edm::LogWarning("HGCalRecHitMapProducer") << " - " << tag; + } + edm::LogWarning("HGCalRecHitMapProducer") << "Returning an empty map."; evt.put(std::make_unique>(), "RefProdVectorPFRecHitCollection"); evt.put(std::move(hitMapPF), "pfRecHitMap"); } else { diff --git a/Validation/RecoParticleFlow/plugins/DigisTester.cc b/Validation/RecoParticleFlow/plugins/DigisTester.cc index eaeecc0d76a43..63a3fc2ac1fe3 100644 --- a/Validation/RecoParticleFlow/plugins/DigisTester.cc +++ b/Validation/RecoParticleFlow/plugins/DigisTester.cc @@ -18,12 +18,12 @@ class DigisTester : public DQMEDAnalyzer { public: - explicit DigisTester(const edm::ParameterSet&); + explicit DigisTester(const edm::ParameterSet&); protected: void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; void analyze(const edm::Event&, const edm::EventSetup&) override; - + edm::ESGetToken geometry_token_; edm::EDGetTokenT ecalEBDigisToken_; edm::EDGetTokenT ecalEEDigisToken_; @@ -35,14 +35,12 @@ class DigisTester : public DQMEDAnalyzer { }; DigisTester::DigisTester(const edm::ParameterSet& iConfig) - : geometry_token_(esConsumes()), - ecalEBDigisToken_(consumes(iConfig.getParameter("ecalEBDigis"))), - ecalEEDigisToken_(consumes(iConfig.getParameter("ecalEEDigis"))), - outFolder_(iConfig.getParameter("outFolder")) {} - -void DigisTester::bookHistograms(DQMStore::IBooker& ibook, - edm::Run const&, - edm::EventSetup const&) { + : geometry_token_(esConsumes()), + ecalEBDigisToken_(consumes(iConfig.getParameter("ecalEBDigis"))), + ecalEEDigisToken_(consumes(iConfig.getParameter("ecalEEDigis"))), + outFolder_(iConfig.getParameter("outFolder")) {} + +void DigisTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { ibook.setCurrentFolder(outFolder_ + "/Digis"); unsigned mNBinsADC = 100; @@ -54,9 +52,9 @@ void DigisTester::bookHistograms(DQMStore::IBooker& ibook, float mEtaMax = 3.3; std::unordered_map> ebVars = { - {"ADC_Eta", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsEta, -mEtaMax, mEtaMax)}, - {"ADC_Phi", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsPhi, -mPhiMax, mPhiMax)}, - {"Eta_Phi", std::make_tuple(mNBinsEta, -mEtaMax, mEtaMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + {"ADC_Eta", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsEta, -mEtaMax, mEtaMax)}, + {"ADC_Phi", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + {"Eta_Phi", std::make_tuple(mNBinsEta, -mEtaMax, mEtaMax, mNBinsPhi, -mPhiMax, mPhiMax)}, }; for (auto& ebVar : ebVars) { @@ -64,15 +62,19 @@ void DigisTester::bookHistograms(DQMStore::IBooker& ibook, auto x_title = ebVar.first.substr(0, ebVar.first.find("_")); auto y_title = ebVar.first.substr(ebVar.first.find("_") + 1); h2d_ebdigis_[ebVar.first] = ibook.book2D("EcalEBDigis" + ebVar.first, - "EcalEBDigis;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); + "EcalEBDigis;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); } std::unordered_map> eeVars = { - {"ADC_Eta", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsEta, -mEtaMax, mEtaMax)}, - {"ADC_Phi", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsPhi, -mPhiMax, mPhiMax)}, - {"Eta_Phi", std::make_tuple(mNBinsEta, -mEtaMax, mEtaMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + {"ADC_Eta", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsEta, -mEtaMax, mEtaMax)}, + {"ADC_Phi", std::make_tuple(mNBinsADC, mADCMin, mADCMax, mNBinsPhi, -mPhiMax, mPhiMax)}, + {"Eta_Phi", std::make_tuple(mNBinsEta, -mEtaMax, mEtaMax, mNBinsPhi, -mPhiMax, mPhiMax)}, }; for (auto& eeVar : eeVars) { @@ -80,9 +82,13 @@ void DigisTester::bookHistograms(DQMStore::IBooker& ibook, auto x_title = eeVar.first.substr(0, eeVar.first.find("_")); auto y_title = eeVar.first.substr(eeVar.first.find("_") + 1); h2d_eedigis_[eeVar.first] = ibook.book2D("EcalEEDigis" + eeVar.first, - "EcalEEDigis;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); + "EcalEEDigis;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); } } @@ -90,7 +96,7 @@ void DigisTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetu const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); // const auto& barrelGeom = caloGeom.getSubdetectorGeometry(DetId::Ecal, EcalBarrel); // const auto& endcapGeom = caloGeom.getSubdetectorGeometry(DetId::Ecal, EcalEndcap); - + edm::Handle ebDigisHandle; iEvent.getByToken(ecalEBDigisToken_, ebDigisHandle); if (!ebDigisHandle.isValid()) { @@ -106,41 +112,43 @@ void DigisTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetu } for (const edm::DataFrame& digi : *ebDigisHandle) { - float eta = caloGeom.getPosition(digi.id()).eta(); - float phi = caloGeom.getPosition(digi.id()).phi(); - + float eta = caloGeom.getPosition(digi.id()).eta(); + float phi = caloGeom.getPosition(digi.id()).phi(); + // Get the number of ADC samples (usually 10) int nADCSamples = digi.size(); - float adcMax = 0; - for (int i=0; i(digi).sample(i).adc(); - if (adcVal > adcMax) adcMax = adcVal; - } - - h2d_ebdigis_["ADC_Eta"]->Fill(adcMax, eta); - h2d_ebdigis_["ADC_Phi"]->Fill(adcMax, phi); - h2d_ebdigis_["Eta_Phi"]->Fill(eta, phi); + float adcMax = 0; + for (int i = 0; i < nADCSamples; ++i) { + // compute max of adc counts of all channels + float adcVal = static_cast(digi).sample(i).adc(); + if (adcVal > adcMax) + adcMax = adcVal; + } + + h2d_ebdigis_["ADC_Eta"]->Fill(adcMax, eta); + h2d_ebdigis_["ADC_Phi"]->Fill(adcMax, phi); + h2d_ebdigis_["Eta_Phi"]->Fill(eta, phi); } for (const edm::DataFrame& digi : *eeDigisHandle) { - float eta = caloGeom.getPosition(digi.id()).eta(); - float phi = caloGeom.getPosition(digi.id()).phi(); + float eta = caloGeom.getPosition(digi.id()).eta(); + float phi = caloGeom.getPosition(digi.id()).phi(); - // Get the number of ADC samples (usually 10) + // Get the number of ADC samples (usually 10) int nADCSamples = digi.size(); - float adcMax = 0; - for (int i=0; i(digi).sample(i).adc(); - if (adcVal > adcMax) adcMax = adcVal; - } - - h2d_eedigis_["ADC_Eta"]->Fill(adcMax, eta); - h2d_eedigis_["ADC_Phi"]->Fill(adcMax, phi); - h2d_eedigis_["Eta_Phi"]->Fill(eta, phi); + float adcMax = 0; + for (int i = 0; i < nADCSamples; ++i) { + // compute max of adc counts of all channels + float adcVal = static_cast(digi).sample(i).adc(); + if (adcVal > adcMax) + adcMax = adcVal; + } + + h2d_eedigis_["ADC_Eta"]->Fill(adcMax, eta); + h2d_eedigis_["ADC_Phi"]->Fill(adcMax, phi); + h2d_eedigis_["Eta_Phi"]->Fill(eta, phi); } } diff --git a/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc b/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc index 07f548c660252..5816e7442a503 100644 --- a/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc +++ b/Validation/RecoParticleFlow/plugins/EcalGeometryAnalyzer.cc @@ -83,7 +83,7 @@ class EcalGeometryAnalyzer : public edm::one::EDAnalyzer using UMap = std::unordered_map; - + UMap nHits_; UMap> detids_; UMap> energies_; @@ -98,18 +98,17 @@ class EcalGeometryAnalyzer : public edm::one::EDAnalyzer()), - caloParticleToken_(consumes(iConfig.getParameter("caloParticles"))), - recHitToken_(consumes(iConfig.getParameter("recHits"))), - simHitToken_(consumes>(iConfig.getParameter("simHits"))), - recClusterToken_(consumes(iConfig.getParameter("recClusters"))), - simClusterToken_(consumes(iConfig.getParameter("simClusters"))), - kinematicCuts_(iConfig.getUntrackedParameter("kinematicCuts")), - enFracCut_(iConfig.getUntrackedParameter("enFracCut")), - ptCut_(iConfig.getUntrackedParameter("ptCut")), - scoreCut_(iConfig.getUntrackedParameter("scoreCut")), - responseCut_(iConfig.getUntrackedParameter("responseCut")) -{ + : caloGeomToken_(esConsumes()), + caloParticleToken_(consumes(iConfig.getParameter("caloParticles"))), + recHitToken_(consumes(iConfig.getParameter("recHits"))), + simHitToken_(consumes>(iConfig.getParameter("simHits"))), + recClusterToken_(consumes(iConfig.getParameter("recClusters"))), + simClusterToken_(consumes(iConfig.getParameter("simClusters"))), + kinematicCuts_(iConfig.getUntrackedParameter("kinematicCuts")), + enFracCut_(iConfig.getUntrackedParameter("enFracCut")), + ptCut_(iConfig.getUntrackedParameter("ptCut")), + scoreCut_(iConfig.getUntrackedParameter("scoreCut")), + responseCut_(iConfig.getUntrackedParameter("responseCut")) { edm::Service fs; geomTree_ = fs->make("Geometry", "Geometry data"); eventTree_ = fs->make("Event", "Event data"); @@ -122,7 +121,8 @@ EcalGeometryAnalyzer::EcalGeometryAnalyzer(const edm::ParameterSet& iConfig) // this cut avoids the need to have associator information in the event if // it is not needed if (needsAssociator(kinematicCuts_, responseCut_)) { - SimToRecoAssociatorToken_ = consumes>(iConfig.getParameter("clusterAssociator")); + SimToRecoAssociatorToken_ = consumes>( + iConfig.getParameter("clusterAssociator")); } } @@ -158,17 +158,17 @@ void EcalGeometryAnalyzer::beginJob() { eventTree_->Branch("eventId", &eventId_); for (auto& prefix : prefixes_) { - eventTree_->Branch(("nHits" + prefix).c_str(), &nHits_[prefix]); - eventTree_->Branch(("detids" + prefix).c_str(), &detids_[prefix]); - eventTree_->Branch(("energies" + prefix).c_str(), &energies_[prefix]); - - eventTree_->Branch(("clusterEnergies" + prefix).c_str(), &clusterEnergies_[prefix]); - eventTree_->Branch(("clusterEtas" + prefix).c_str(), &clusterEtas_[prefix]); - eventTree_->Branch(("clusterPhis" + prefix).c_str(), &clusterPhis_[prefix]); - eventTree_->Branch(("clusterHitDetids" + prefix).c_str(), &clusterHitDetids_[prefix]); - eventTree_->Branch(("clusterHitClids" + prefix).c_str(), &clusterHitClids_[prefix]); - eventTree_->Branch(("clusterHitEnergies" + prefix).c_str(), &clusterHitEnergies_[prefix]); - eventTree_->Branch(("clusterHitFractions" + prefix).c_str(), &clusterHitFractions_[prefix]); + eventTree_->Branch(("nHits" + prefix).c_str(), &nHits_[prefix]); + eventTree_->Branch(("detids" + prefix).c_str(), &detids_[prefix]); + eventTree_->Branch(("energies" + prefix).c_str(), &energies_[prefix]); + + eventTree_->Branch(("clusterEnergies" + prefix).c_str(), &clusterEnergies_[prefix]); + eventTree_->Branch(("clusterEtas" + prefix).c_str(), &clusterEtas_[prefix]); + eventTree_->Branch(("clusterPhis" + prefix).c_str(), &clusterPhis_[prefix]); + eventTree_->Branch(("clusterHitDetids" + prefix).c_str(), &clusterHitDetids_[prefix]); + eventTree_->Branch(("clusterHitClids" + prefix).c_str(), &clusterHitClids_[prefix]); + eventTree_->Branch(("clusterHitEnergies" + prefix).c_str(), &clusterHitEnergies_[prefix]); + eventTree_->Branch(("clusterHitFractions" + prefix).c_str(), &clusterHitFractions_[prefix]); } } @@ -177,9 +177,7 @@ double EcalGeometryAnalyzer::inBarrel(const DetId& id) { return id.det() == DetId::Ecal && id.subdetId() == EcalBarrel; } -bool EcalGeometryAnalyzer::needsAssociator(bool kinCuts, double respCut) { - return kinCuts || respCut > 0.; -} +bool EcalGeometryAnalyzer::needsAssociator(bool kinCuts, double respCut) { return kinCuts || respCut > 0.; } void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { // Get the ECAL geometry @@ -189,16 +187,16 @@ void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSet // Reset vector variables for (auto& prefix : prefixes_) { - detids_[prefix].clear(); - energies_[prefix].clear(); - - clusterEnergies_[prefix].clear(); - clusterEtas_[prefix].clear(); - clusterPhis_[prefix].clear(); - clusterHitEnergies_[prefix].clear(); - clusterHitFractions_[prefix].clear(); - clusterHitDetids_[prefix].clear(); - clusterHitClids_[prefix].clear(); + detids_[prefix].clear(); + energies_[prefix].clear(); + + clusterEnergies_[prefix].clear(); + clusterEtas_[prefix].clear(); + clusterPhis_[prefix].clear(); + clusterHitEnergies_[prefix].clear(); + clusterHitFractions_[prefix].clear(); + clusterHitDetids_[prefix].clear(); + clusterHitClids_[prefix].clear(); } unsigned eventId = iEvent.id().event(); @@ -259,7 +257,7 @@ void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSet iEvent.getByToken(caloParticleToken_, caloParticles_); if (!caloParticles_.isValid()) { edm::LogPrint("EcalGeometryAnalyzer") << "Input CaloParticle collection not found."; - return; + return; } auto caloParticles = *caloParticles_; @@ -272,15 +270,14 @@ void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSet edm::Handle> SimToRecoAssociatorCollection; ticl::SimToRecoCollectionWithSimClustersT simToRecoAssoc; if (needsAssociator(kinematicCuts_, responseCut_)) { - iEvent.getByToken(SimToRecoAssociatorToken_, SimToRecoAssociatorCollection); - if (!SimToRecoAssociatorCollection.isValid()) { - edm::LogPrint("EcalGeometryAnalyzer") << "Input clusterAssociator SimToReco collection not found."; - return; - } - simToRecoAssoc = *SimToRecoAssociatorCollection; - } - else { - simToRecoAssoc = ticl::SimToRecoCollectionWithSimClustersT(); + iEvent.getByToken(SimToRecoAssociatorToken_, SimToRecoAssociatorCollection); + if (!SimToRecoAssociatorCollection.isValid()) { + edm::LogPrint("EcalGeometryAnalyzer") << "Input clusterAssociator SimToReco collection not found."; + return; + } + simToRecoAssoc = *SimToRecoAssociatorCollection; + } else { + simToRecoAssoc = ticl::SimToRecoCollectionWithSimClustersT(); } // Build map linking each sim cluster to the energy of their mother calo particle @@ -291,7 +288,7 @@ void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSet for (const auto& scRef : caloParticles[cpId].simClusters()) { auto const& sc = *(scRef); for (auto hit_energy : sc.hits_and_energies()) { - energySumSimHits += hit_energy.second; + energySumSimHits += hit_energy.second; } } for (const auto& scRef : caloParticles[cpId].simClusters()) { @@ -321,24 +318,24 @@ void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSet // reco clusters unsigned recClusterCounter = 0; for (auto& rcl : recClusters) { - // properties of the clusters - clusterEnergies_["Reco"].push_back(rcl.energy()); - clusterEtas_["Reco"].push_back(rcl.eta()); - clusterPhis_["Reco"].push_back(rcl.phi()); - - recClusterCounter++; - - for (auto const& rechit : rcl.recHitFractions()) { - const auto& ref = rechit.recHitRef(); - DetId clhitId(ref->detId()); - if (!inBarrel(clhitId)) - continue; - - clusterHitDetids_["Reco"].push_back(clhitId); - clusterHitClids_["Reco"].push_back(recClusterCounter); - clusterHitEnergies_["Reco"].push_back(ref->energy()); - clusterHitFractions_["Reco"].push_back(rechit.fraction()); - } + // properties of the clusters + clusterEnergies_["Reco"].push_back(rcl.energy()); + clusterEtas_["Reco"].push_back(rcl.eta()); + clusterPhis_["Reco"].push_back(rcl.phi()); + + recClusterCounter++; + + for (auto const& rechit : rcl.recHitFractions()) { + const auto& ref = rechit.recHitRef(); + DetId clhitId(ref->detId()); + if (!inBarrel(clhitId)) + continue; + + clusterHitDetids_["Reco"].push_back(clhitId); + clusterHitClids_["Reco"].push_back(recClusterCounter); + clusterHitEnergies_["Reco"].push_back(ref->energy()); + clusterHitFractions_["Reco"].push_back(rechit.fraction()); + } } // sim clusters @@ -348,106 +345,104 @@ void EcalGeometryAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSet */ bool passResponseMatch = false; if (needsAssociator(kinematicCuts_, responseCut_)) { - for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - const edm::Ref simClusterRef(simClusters_, simId); - const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); - if (simToRecoIt == simToRecoAssoc.end()) - continue; - const auto& simToRecoMatched = simToRecoIt->val; - if (simToRecoMatched.empty()) - continue; - - for (const auto& recoPair : simToRecoMatched) { - auto recoId = recoPair.first.index(); - double response = recClusters[recoId].energy() / simClusters[simId].energy(); - if (response >= responseCut_) { - passResponseMatch = true; - break; - } - } - - if (passResponseMatch) - break; - } - } - else { - passResponseMatch = true; + for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { + const edm::Ref simClusterRef(simClusters_, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + + for (const auto& recoPair : simToRecoMatched) { + auto recoId = recoPair.first.index(); + double response = recClusters[recoId].energy() / simClusters[simId].energy(); + if (response >= responseCut_) { + passResponseMatch = true; + break; + } + } + + if (passResponseMatch) + break; + } + } else { + passResponseMatch = true; } - unsigned simClusterCounter = 0; + unsigned simClusterCounter = 0; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - if (!passResponseMatch) - break; - - auto& scl = simClusters[simId]; - if (kinematicCuts_) - { - double energySumSimHits = 0; - for (auto hit_energy : scl.hits_and_energies()) { - energySumSimHits += hit_energy.second; - } - - // apply cut on energy fraction - // (sim cluster energy wrt all sim clusters from same calo particle) - double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; - if (SimClusterToCPEnergyFraction < enFracCut_) - continue; - // apply cut on pt of the sim track - if (simClusters[simId].pt() < ptCut_) - continue; - - // filter all sim clusters produced by a sim track which crossed the - // tracker/calorimeter boundary outside the barrel - auto const scTrack = simClusters[simId].g4Tracks()[0]; - const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); - auto const simTrackEtaAtBoundary = pos.Eta(); - if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel - continue; - - const edm::Ref simClusterRef(simClusters_, simId); - const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); - if (simToRecoIt == simToRecoAssoc.end()) - continue; - const auto& simToRecoMatched = simToRecoIt->val; - if (simToRecoMatched.empty()) - continue; - - // remove the cluster (not the event!) - // if the sim cluster is not matched to a reco cluster - // with a score lower than "scoreCut" - bool passScoreMatch = false; - for (const auto& recoPair : simToRecoMatched) { - if (recoPair.second.second <= scoreCut_) { - passScoreMatch = true; - break; - } - } - if (!passScoreMatch) - continue; - } - - // properties of the clusters - clusterEnergies_["Sim"].push_back(scl.energy()); - clusterEtas_["Sim"].push_back(scl.eta()); - clusterPhis_["Sim"].push_back(scl.phi()); - - simClusterCounter++; - - // properties of the hits in each cluster - const auto& hits_fractions = scl.hits_and_fractions(); - const auto& hits_energies = scl.hits_and_energies(); - - auto itF = hits_fractions.begin(); - auto itE = hits_energies.begin(); - for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { - DetId clhitId(itF->first); - if (!inBarrel(clhitId)) - continue; - clusterHitDetids_["Sim"].push_back(clhitId); - clusterHitClids_["Sim"].push_back(simClusterCounter); - clusterHitEnergies_["Sim"].push_back(itE->second); - clusterHitFractions_["Sim"].push_back(itF->second); - } + if (!passResponseMatch) + break; + + auto& scl = simClusters[simId]; + if (kinematicCuts_) { + double energySumSimHits = 0; + for (auto hit_energy : scl.hits_and_energies()) { + energySumSimHits += hit_energy.second; + } + + // apply cut on energy fraction + // (sim cluster energy wrt all sim clusters from same calo particle) + double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; + if (SimClusterToCPEnergyFraction < enFracCut_) + continue; + // apply cut on pt of the sim track + if (simClusters[simId].pt() < ptCut_) + continue; + + // filter all sim clusters produced by a sim track which crossed the + // tracker/calorimeter boundary outside the barrel + auto const scTrack = simClusters[simId].g4Tracks()[0]; + const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); + auto const simTrackEtaAtBoundary = pos.Eta(); + if (abs(simTrackEtaAtBoundary) > 1.48) // simTrack does not cross the barrel + continue; + + const edm::Ref simClusterRef(simClusters_, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + + // remove the cluster (not the event!) + // if the sim cluster is not matched to a reco cluster + // with a score lower than "scoreCut" + bool passScoreMatch = false; + for (const auto& recoPair : simToRecoMatched) { + if (recoPair.second.second <= scoreCut_) { + passScoreMatch = true; + break; + } + } + if (!passScoreMatch) + continue; + } + + // properties of the clusters + clusterEnergies_["Sim"].push_back(scl.energy()); + clusterEtas_["Sim"].push_back(scl.eta()); + clusterPhis_["Sim"].push_back(scl.phi()); + + simClusterCounter++; + + // properties of the hits in each cluster + const auto& hits_fractions = scl.hits_and_fractions(); + const auto& hits_energies = scl.hits_and_energies(); + + auto itF = hits_fractions.begin(); + auto itE = hits_energies.begin(); + for (; itF != hits_fractions.end() && itE != hits_energies.end(); ++itF, ++itE) { + DetId clhitId(itF->first); + if (!inBarrel(clhitId)) + continue; + clusterHitDetids_["Sim"].push_back(clhitId); + clusterHitClids_["Sim"].push_back(simClusterCounter); + clusterHitEnergies_["Sim"].push_back(itE->second); + clusterHitFractions_["Sim"].push_back(itF->second); + } } eventTree_->Fill(); diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 416470edf50dd..84708cc69ab3b 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -35,7 +35,7 @@ class PFTesterT : public DQMEDAnalyzer { std::string doubleToString(double x) const; double simClusterEnergy(const SimCluster& sc) const; double recoClusterEnergyWeightedBySimFraction(const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const; - + edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; edm::EDGetTokenT RechitToken_; @@ -116,7 +116,7 @@ class PFTesterT : public DQMEDAnalyzer { {"Eta", std::make_tuple(50, -6.5, 6.5)}, {"Phi", std::make_tuple(50, -3.5, 3.5)}, {"Mult", std::make_tuple(200, 0., 200.)}, - {"Time", std::make_tuple(60, -60., 60.)}, + {"Time", std::make_tuple(60, -60., 60.)}, }; const std::unordered_map> histoVarsSim = { {"En", std::make_tuple(100, 0., 100.)}, @@ -141,46 +141,46 @@ class PFTesterT : public DQMEDAnalyzer { std::vector h_nRecoMatchedToOneSim_; std::unordered_map> h2dVarsRecoHits = { - {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, - {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, - {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, - {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, - {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, + {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, + {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, }; std::unordered_map> h2dVarsRecoClusters = { - {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, - {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, - {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, - {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, - {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, - {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, - {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, - {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, - {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, - {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, - {"Mult_Time", std::make_tuple(200, 0., 200., 200, -100., 100.)}, + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, + {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, + {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, + {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, + {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + {"En_Time", std::make_tuple(100, 0., 100., 200, -100., 100.)}, + {"Eta_Time", std::make_tuple(50, -6.5, 6.5, 200, -100., 100.)}, + {"Phi_Time", std::make_tuple(50, -3.5, 3.5, 200, -100., 100.)}, + {"Mult_Time", std::make_tuple(200, 0., 200., 200, -100., 100.)}, }; std::unordered_map> h2dVarsSim = { - {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, - {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, - {"EnFrac_Eta", std::make_tuple(220, 0., 1.1, 50, -6.5, 6.5)}, - {"EnFrac_Phi", std::make_tuple(220, 0., 1.1, 50, -3.5, 3.5)}, - {"EnFrac_Mult", std::make_tuple(220, 0., 1.1, 200, 0., 200.)}, - {"EnSimTrack_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, - {"EnSimTrack_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"En_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, + {"En_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, + {"Eta_Phi", std::make_tuple(50, -6.5, 6.5, 50, -3.5, 3.5)}, + {"EnFrac_Eta", std::make_tuple(220, 0., 1.1, 50, -6.5, 6.5)}, + {"EnFrac_Phi", std::make_tuple(220, 0., 1.1, 50, -3.5, 3.5)}, + {"EnFrac_Mult", std::make_tuple(220, 0., 1.1, 200, 0., 200.)}, + {"EnSimTrack_Eta", std::make_tuple(100, 0., 100., 50, -6.5, 6.5)}, + {"EnSimTrack_Phi", std::make_tuple(100, 0., 100., 50, -3.5, 3.5)}, {"EnSimTrack_Mult", std::make_tuple(100, 0., 100., 200, 0., 200.)}, - {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, - {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, - {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, - {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, - {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, + {"Pt_Eta", std::make_tuple(100, 0., 40., 50, -6.5, 6.5)}, + {"Pt_Phi", std::make_tuple(100, 0., 40., 50, -3.5, 3.5)}, + {"Pt_Mult", std::make_tuple(100, 0., 40., 200, 0., 200.)}, + {"Mult_Eta", std::make_tuple(200, 0., 200., 50, -6.5, 6.5)}, + {"Mult_Phi", std::make_tuple(200, 0., 200., 50, -3.5, 3.5)}, }; using U2Map = std::unordered_map; @@ -191,7 +191,7 @@ class PFTesterT : public DQMEDAnalyzer { VU2Map h2d_recoClustersMatchedSimClusters_; U2Map h2d_recoHitsInClusters_; - + VU2Map h2d_responsePt_; VU2Map h2d_responseE_; }; @@ -243,12 +243,11 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 100, 0, 2); - h_CPToSHEnergyFraction_ = - ibook.book1D("CaloParticleToSimHitsEnergyFraction", - "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", - 100, - 0, - 2); + h_CPToSHEnergyFraction_ = ibook.book1D("CaloParticleToSimHitsEnergyFraction", + "CaloParticleToSimHitsEnergyFraction;CaloParticle to SimHits energy fraction", + 100, + 0, + 2); h_CP_recoToSimScore_ = ibook.book1D("CP_recoToSimScore", "CPrecoToSimScore;CaloParticle Reco #rightarrow Sim score", 51, 0, 1.02); h_CP_simToRecoScore_ = @@ -305,15 +304,15 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 220, 0., 1.1); - h_simToRecoShEnF_EnSimTrack_ = - ibook.book2D("simToRecoShEnF_EnSimTrack", - "simToRecoSharedEnergy vs SimTrack Energy;Sim #rightarrow Reco shared energy fraction;SimTrack Energy", - 51, - 0, - 1.02, - 100, - 0., - 100.); + h_simToRecoShEnF_EnSimTrack_ = ibook.book2D( + "simToRecoShEnF_EnSimTrack", + "simToRecoSharedEnergy vs SimTrack Energy;Sim #rightarrow Reco shared energy fraction;SimTrack Energy", + 51, + 0, + 1.02, + 100, + 0., + 100.); h_simToRecoShEnF_Mult_ = ibook.book2D("simToRecoShEnF_Mult", "simToRecoSharedEnergy vs Multiplicity;Sim #rightarrow Reco shared energy fraction;Multiplicity", @@ -324,13 +323,13 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 0., 200.); h_simToRecoScore_En_ = ibook.book2D("simToRecoScore_En", - "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy_{hits}", - 51, - 0, - 1.02, - 100, - 0., - 100.); + "simToRecoScore vs Energy;Sim #rightarrow Reco score;Energy_{hits}", + 51, + 0, + 1.02, + 100, + 0., + 100.); h_simToRecoScore_EnFrac_ = ibook.book2D("simToRecoScore_EnFrac", "simToRecoScore vs Energy Fraction;Sim #rightarrow Reco score;EnFrac", 51, @@ -339,8 +338,15 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, 220, 0., 1.1); - h_simToRecoScore_EnSimTrack_ = ibook.book2D( - "simToRecoScore_EnSimTrack", "simToRecoScore vs SimTrack Energy;Sim #rightarrow Reco score;SimTrack Energy", 51, 0, 1.02, 100, 0., 100.); + h_simToRecoScore_EnSimTrack_ = + ibook.book2D("simToRecoScore_EnSimTrack", + "simToRecoScore vs SimTrack Energy;Sim #rightarrow Reco score;SimTrack Energy", + 51, + 0, + 1.02, + 100, + 0., + 100.); h_simToRecoScore_Mult_ = ibook.book2D("simToRecoScore_Mult", "simToRecoScore vs Multiplicity;Sim #rightarrow Reco score;Multiplicity", 51, @@ -386,7 +392,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_recoClusters_[h2dVar.first] = ibook.book2D("RecoClusters" + h2dVar.first, - "RecoClusters;" + x_title + ";" + y_title, + "RecoClusters;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, @@ -395,34 +401,34 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMaxY); } for (auto& h2dVar : h2dVarsRecoHits) { - auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; - auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); h2d_recoHitsInClusters_[h2dVar.first] = ibook.book2D("RecoHitsInClusters" + h2dVar.first, - "RecoHitsInClusters;" + x_title + ";" + y_title, - nBinsX, - hMinX, - hMaxX, - nBinsY, - hMinY, - hMaxY); + "RecoHitsInClusters;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); } - + for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); h_nSimMatchedToOneReco_[ithr] = ibook.book1D( - "nSimMatchedToOneReco", - "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", - 10, - 0, - 10); + "nSimMatchedToOneReco", + "Number of SimClusters matched to a RecoCluster;Number of RecoClusters; Number of matched SimClusters", + 10, + 0, + 10); h_nRecoMatchedToOneSim_[ithr] = ibook.book1D( - "nRecoMatchedToOneSim", - "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", - 10, - 0, - 10); + "nRecoMatchedToOneSim", + "Number of RecoClusters matched to a SimCluster;Number of SimClusters; Number of matched RecoClusters", + 10, + 0, + 10); for (auto& hVar : histoVarsSim) { auto [nBins, hMin, hMax] = hVar.second; h_simClustersMatchedRecoClusters_[ithr][hVar.first] = @@ -624,9 +630,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // Compute energy of caloParticle as sum of simClusters energies (from SimTrack energy) energySumSimClusters += sc.energy(); // Compute energy of caloParticle as sum of all hits from all simClusters - energySumSimHits += simClusterEnergy(sc); + energySumSimHits += simClusterEnergy(sc); // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters - recoEnergySumWeightedBySimFrac += recoClusterEnergyWeightedBySimFraction(sc, pfRechit); + recoEnergySumWeightedBySimFrac += recoClusterEnergyWeightedBySimFraction(sc, pfRechit); } for (const auto& scRef : caloParticles[cpId].simClusters()) { simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; @@ -653,8 +659,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (const auto& recoPair : cpToRecoMatched) { #ifdef debug edm::LogPrint("PFTester") << " caloParticle [" << cpId << "] matched to RecoCluster [" << recoPair.first.index() - << "] with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / recoEnergySumWeightedBySimFrac + << "] with shared energy: " << recoPair.second.first << ", shared energy fraction: " + << recoPair.second.first / recoEnergySumWeightedBySimFrac << ", score: " << recoPair.second.second; #endif h_CP_simToRecoScore_->Fill(recoPair.second.second); @@ -683,10 +689,10 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e uint nSimClusters = 0; uint nSimClustersPrimary = 0; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = simClusterEnergy(simClusters[simId]); + double energySumSimHits = simClusterEnergy(simClusters[simId]); h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simClusters[simId].energy()); - double recoEnergySumWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); + double recoEnergySumWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; @@ -720,7 +726,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_simClusters_["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); h2d_simClusters_["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClusters_["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); + h2d_simClusters_["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); h2d_simClusters_["En_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); h2d_simClusters_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); h2d_simClusters_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); @@ -767,8 +773,8 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e } edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() << "], en=" << recoClusters[recoPair.first.index()].energy() - << ", with shared energy: " << recoPair.second.first - << ", shared energy fraction: " << recoPair.second.first / recoEnergySumWeightedBySimFrac + << ", with shared energy: " << recoPair.second.first << ", shared energy fraction: " + << recoPair.second.first / recoEnergySumWeightedBySimFrac << ", score: " << recoPair.second.second; for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { DetId id(hit_energy.recHitRef()->detId()); @@ -821,20 +827,21 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(energySumSimHits, - simClusters[simId].numberOfRecHits()); + simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simClusters[simId].energy(), + simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Phi"]->Fill(simClusters[simId].energy(), - simClusters[simId].phi()); + simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Mult"]->Fill(simClusters[simId].energy(), - simClusters[simId].numberOfRecHits()); + simClusters[simId].numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), @@ -869,50 +876,49 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { - // fake and merge denominator h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); - if constexpr (std::is_same::value) { - h_recoClusters_["Time"]->Fill(recoClusters[recoId].time()); - } - + if constexpr (std::is_same::value) { + h_recoClusters_["Time"]->Fill(recoClusters[recoId].time()); + } + h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); - h2d_recoClusters_["Eta_Phi"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].phi()); + h2d_recoClusters_["Eta_Phi"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].phi()); h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); h2d_recoClusters_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClusters_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - if constexpr (std::is_same::value) { - h2d_recoClusters_["En_Time"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].time()); - h2d_recoClusters_["Eta_Time"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].time()); - h2d_recoClusters_["Phi_Time"]->Fill(recoClusters[recoId].phi(), recoClusters[recoId].time()); - h2d_recoClusters_["Mult_Time"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].time()); - } - - // hits belonging to the clusters - const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); - for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { - DetId id = haf.first; - const GlobalPoint pos = caloGeom.getPosition(id); - - auto rechitIt = - std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); - if (rechitIt == pfRechit.end()) { - edm::LogPrint("PFTester") << " PF Hit not found! | eta=" << pos.eta() << ", phi=" << pos.phi(); - } else { - h2d_recoHitsInClusters_["En_Eta"]->Fill(rechitIt->energy(), pos.eta()); - h2d_recoHitsInClusters_["En_Phi"]->Fill(rechitIt->energy(), pos.phi()); - h2d_recoHitsInClusters_["Eta_Phi"]->Fill(pos.eta(), pos.phi()); - h2d_recoHitsInClusters_["En_Time"]->Fill(rechitIt->energy(), rechitIt->time()); - h2d_recoHitsInClusters_["Eta_Time"]->Fill(pos.eta(), rechitIt->time()); - h2d_recoHitsInClusters_["Phi_Time"]->Fill(pos.phi(), rechitIt->time()); - } - } - - // matching + if constexpr (std::is_same::value) { + h2d_recoClusters_["En_Time"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].time()); + h2d_recoClusters_["Eta_Time"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].time()); + h2d_recoClusters_["Phi_Time"]->Fill(recoClusters[recoId].phi(), recoClusters[recoId].time()); + h2d_recoClusters_["Mult_Time"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].time()); + } + + // hits belonging to the clusters + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + for (const auto& haf : recoClusters[recoId].hitsAndFractions()) { + DetId id = haf.first; + const GlobalPoint pos = caloGeom.getPosition(id); + + auto rechitIt = + std::find_if(pfRechit.begin(), pfRechit.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + if (rechitIt == pfRechit.end()) { + edm::LogPrint("PFTester") << " PF Hit not found! | eta=" << pos.eta() << ", phi=" << pos.phi(); + } else { + h2d_recoHitsInClusters_["En_Eta"]->Fill(rechitIt->energy(), pos.eta()); + h2d_recoHitsInClusters_["En_Phi"]->Fill(rechitIt->energy(), pos.phi()); + h2d_recoHitsInClusters_["Eta_Phi"]->Fill(pos.eta(), pos.phi()); + h2d_recoHitsInClusters_["En_Time"]->Fill(rechitIt->energy(), rechitIt->time()); + h2d_recoHitsInClusters_["Eta_Time"]->Fill(pos.eta(), rechitIt->time()); + h2d_recoHitsInClusters_["Phi_Time"]->Fill(pos.phi(), rechitIt->time()); + } + } + + // matching const edm::Ref recoClusterRef(RecoCluster, recoId); const auto& recoToSimIt = recoToSimAssoc.find(recoClusterRef); if (recoToSimIt == recoToSimAssoc.end()) @@ -934,7 +940,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e << " : matched simCluster id = " << simPairIdx << " score = " << simPair.second; #endif - double energySumSimHits = simClusterEnergy(simClusters[simPairIdx]); + double energySumSimHits = simClusterEnergy(simClusters[simPairIdx]); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simPairIdx]; @@ -947,7 +953,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel auto const scTrack = simClusters[simPairIdx].g4Tracks()[0]; - const math::XYZTLorentzVectorF pos = scTrack.getPositionAtBoundary(); + const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); if (abs(pos.Eta()) > etaCut_) // simTrack does not cross the barrel continue; @@ -970,35 +976,35 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e recoClusters[recoId].eta()); h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoClusters[recoId].eta(), - recoClusters[recoId].phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoClusters[recoId].eta(), + recoClusters[recoId].phi()); h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); - if constexpr (std::is_same::value) { - h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoClusters[recoId].eta(), - recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoClusters[recoId].phi(), - recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoClusters[recoId].size(), - recoClusters[recoId].time()); - } - - // merge numerator + if constexpr (std::is_same::value) { + h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoClusters[recoId].energy(), + recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoClusters[recoId].eta(), + recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoClusters[recoId].phi(), + recoClusters[recoId].time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoClusters[recoId].size(), + recoClusters[recoId].time()); + } + + // merge numerator if (nSimMatchedToOneReco > 1) { h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); - if constexpr (std::is_same::value) { - h_recoClustersMultiMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); - } + if constexpr (std::is_same::value) { + h_recoClustersMultiMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); + } } } @@ -1010,10 +1016,10 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // ----- Cluster response computation --------------------------------- // -------------------------------------------------------------------- for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = simClusterEnergy(simClusters[simId]); + double energySumSimHits = simClusterEnergy(simClusters[simId]); + + double recoEnergyWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); - double recoEnergyWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); - // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; if (SimClusterToCPEnergyFraction < enFracCut_) @@ -1050,7 +1056,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // fill only the best matched (lowest score) reco cluster, regardless split or merge for (const auto& recoPair : simToRecoMatchedSorted) { auto recoId = recoPair.first.index(); - + bool passMatch = false; if (doMatchByScore_) { // cut on score @@ -1061,9 +1067,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e double shared_energy_frac = shared_energy / recoEnergyWeightedBySimFrac; passMatch = shared_energy_frac > thresh; } - + if (passMatch) { - float resp = recoClusters[recoId].energy() / energySumSimHits; + float resp = recoClusters[recoId].energy() / energySumSimHits; h2d_responseE_[ithr]["En"]->Fill(energySumSimHits, resp); h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, resp); h2d_responseE_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), resp); @@ -1072,7 +1078,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_responseE_[ithr]["Phi"]->Fill(simClusters[simId].phi(), resp); h2d_responseE_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits(), resp); break; - } + } } } } @@ -1214,7 +1220,7 @@ template double PFTesterT::simClusterEnergy(const SimCluster& sc) const { double energySumSimHits = 0; for (auto hit_energy : sc.hits_and_energies()) { - energySumSimHits += hit_energy.second; + energySumSimHits += hit_energy.second; } return energySumSimHits; } @@ -1222,32 +1228,35 @@ double PFTesterT::simClusterEnergy(const SimCluster& sc) // compute the denominator of the shared energy fraction, i.e, // the total energy of a reconstructed cluster, weighted by the sim cluster energy fraction template -double PFTesterT::recoClusterEnergyWeightedBySimFraction(const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const { +double PFTesterT::recoClusterEnergyWeightedBySimFraction( + const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const { double recoEnergySumWeightedBySimFrac = 0; for (auto hit_fraction : sc.hits_and_fractions()) { - DetId id(hit_fraction.first); - auto rechitIt = - std::find_if(pfrechits.begin(), pfrechits.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); - if (rechitIt == pfrechits.end()) { - continue; - } else { - recoEnergySumWeightedBySimFrac += rechitIt->energy() * hit_fraction.second; - } + DetId id(hit_fraction.first); + auto rechitIt = + std::find_if(pfrechits.begin(), pfrechits.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); + if (rechitIt == pfrechits.end()) { + continue; + } else { + recoEnergySumWeightedBySimFrac += rechitIt->energy() * hit_fraction.second; + } } return recoEnergySumWeightedBySimFrac; } template void PFTesterT::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { - edm::ParameterSetDescription desc; + edm::ParameterSetDescription desc; desc.add("outFolder", "HLT/ParticleFlow"); desc.add("PFCand", edm::InputTag("hltParticleFlow")); desc.add("Rechit", edm::InputTag("hltParticleFlowRecHitECALUnseeded")); desc.add("RecoCluster", edm::InputTag("hltParticleFlowClusterECALUnseeded")); desc.add("SimCluster", edm::InputTag("mix", "MergedCaloTruth")); - desc.add("CaloParticle", edm::InputTag("mix","MergedCaloTruth")); - desc.add("ClusterSimClusterAssociator", edm::InputTag("hltPFClusterSimClusterAssociationProducerECAL")); - desc.add("ClusterCaloParticleAssociator", edm::InputTag("hltPFClusterCaloParticleAssociationProducerECAL")); + desc.add("CaloParticle", edm::InputTag("mix", "MergedCaloTruth")); + desc.add("ClusterSimClusterAssociator", + edm::InputTag("hltPFClusterSimClusterAssociationProducerECAL")); + desc.add("ClusterCaloParticleAssociator", + edm::InputTag("hltPFClusterCaloParticleAssociationProducerECAL")); desc.add>("assocScoreThresholds", {0.1}); desc.add("doMatchByScore", true); desc.add("enFracCut", 0.01); diff --git a/Validation/RecoParticleFlow/plugins/RecHitTester.cc b/Validation/RecoParticleFlow/plugins/RecHitTester.cc index a19a778cbd3ff..fa8de8e9a0427 100644 --- a/Validation/RecoParticleFlow/plugins/RecHitTester.cc +++ b/Validation/RecoParticleFlow/plugins/RecHitTester.cc @@ -21,19 +21,19 @@ class RecHitTester : public DQMEDAnalyzer { public: explicit RecHitTester(const edm::ParameterSet&); static void fillDescriptions(edm::ConfigurationDescriptions& descriptions); - + protected: void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; void analyze(const edm::Event&, const edm::EventSetup&) override; - + private: using SimHitsT = std::vector; using RecHitsT = EcalRecHitCollection; using PFRecHitsT = reco::PFRecHitCollection; using UncalibRecHitsT = EcalUncalibratedRecHitCollection; - + edm::ESGetToken caloGeomToken_; - + const edm::EDGetTokenT ebSimHitToken_, eeSimHitToken_; const edm::EDGetTokenT ebUncalibRecHitToken_, eeUncalibRecHitToken_; const edm::EDGetTokenT ebRecHitToken_, eeRecHitToken_; @@ -55,54 +55,49 @@ class RecHitTester : public DQMEDAnalyzer { }; RecHitTester::RecHitTester(const edm::ParameterSet& iConfig) - : caloGeomToken_(esConsumes()), - ebSimHitToken_(consumes(iConfig.getParameter("ebSimHits"))), - eeSimHitToken_(consumes(iConfig.getParameter("eeSimHits"))), - ebUncalibRecHitToken_(consumes(iConfig.getParameter("ebUncalibRecHits"))), - eeUncalibRecHitToken_(consumes(iConfig.getParameter("eeUncalibRecHits"))), - ebRecHitToken_(consumes(iConfig.getParameter("ebRecHits"))), - eeRecHitToken_(consumes(iConfig.getParameter("eeRecHits"))), - PFRecHitToken_(consumes(iConfig.getParameter("pfRecHits"))), - outFolder_(iConfig.getParameter("outFolder")) {} - -void RecHitTester::bookHistograms(DQMStore::IBooker& ibook, - edm::Run const&, - edm::EventSetup const&) { - + : caloGeomToken_(esConsumes()), + ebSimHitToken_(consumes(iConfig.getParameter("ebSimHits"))), + eeSimHitToken_(consumes(iConfig.getParameter("eeSimHits"))), + ebUncalibRecHitToken_(consumes(iConfig.getParameter("ebUncalibRecHits"))), + eeUncalibRecHitToken_(consumes(iConfig.getParameter("eeUncalibRecHits"))), + ebRecHitToken_(consumes(iConfig.getParameter("ebRecHits"))), + eeRecHitToken_(consumes(iConfig.getParameter("eeRecHits"))), + PFRecHitToken_(consumes(iConfig.getParameter("pfRecHits"))), + outFolder_(iConfig.getParameter("outFolder")) {} + +void RecHitTester::bookHistograms(DQMStore::IBooker& ibook, edm::Run const&, edm::EventSetup const&) { ibook.setCurrentFolder(outFolder_ + "/Hits"); for (auto& h2dVar : histo2dVarsReco) { - auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; - auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); + auto [nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY] = h2dVar.second; + auto x_title = h2dVar.first.substr(0, h2dVar.first.find("_")); auto y_title = h2dVar.first.substr(h2dVar.first.find("_") + 1); - h2d_ebsimHits_[h2dVar.first] = ibook.book2D("EBSimHits" + h2dVar.first, - "EBSimHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); - h2d_eesimHits_[h2dVar.first] = ibook.book2D("EESimHits" + h2dVar.first, - "EESimHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); - h2d_ebuncalibRecHits_[h2dVar.first] = ibook.book2D("EBUncalibRecHits" + h2dVar.first, - "EBUncalibRecHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); - h2d_eeuncalibRecHits_[h2dVar.first] = ibook.book2D("EEUncalibRecHits" + h2dVar.first, - "EEUncalibRecHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); - h2d_ebrecHits_[h2dVar.first] = ibook.book2D("EBRecHits" + h2dVar.first, - "EBRecHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); - h2d_eerecHits_[h2dVar.first] = ibook.book2D("EERecHits" + h2dVar.first, - "EERecHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); - h2d_pfRecHits_[h2dVar.first] = ibook.book2D("PFRecHits" + h2dVar.first, - "PFRecHits;" + x_title + ";" + y_title, - nBinsX, hMinX, hMaxX, - nBinsY, hMinY, hMaxY); + h2d_ebsimHits_[h2dVar.first] = ibook.book2D( + "EBSimHits" + h2dVar.first, "EBSimHits;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY); + h2d_eesimHits_[h2dVar.first] = ibook.book2D( + "EESimHits" + h2dVar.first, "EESimHits;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY); + h2d_ebuncalibRecHits_[h2dVar.first] = ibook.book2D("EBUncalibRecHits" + h2dVar.first, + "EBUncalibRecHits;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + h2d_eeuncalibRecHits_[h2dVar.first] = ibook.book2D("EEUncalibRecHits" + h2dVar.first, + "EEUncalibRecHits;" + x_title + ";" + y_title, + nBinsX, + hMinX, + hMaxX, + nBinsY, + hMinY, + hMaxY); + h2d_ebrecHits_[h2dVar.first] = ibook.book2D( + "EBRecHits" + h2dVar.first, "EBRecHits;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY); + h2d_eerecHits_[h2dVar.first] = ibook.book2D( + "EERecHits" + h2dVar.first, "EERecHits;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY); + h2d_pfRecHits_[h2dVar.first] = ibook.book2D( + "PFRecHits" + h2dVar.first, "PFRecHits;" + x_title + ";" + y_title, nBinsX, hMinX, hMaxX, nBinsY, hMinY, hMaxY); } } @@ -134,7 +129,7 @@ void RecHitTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSet edm::LogPrint("RecHitTester") << "Input EE UncalibRecHit collection not found."; return; } - + edm::Handle ebrechitHandle; iEvent.getByToken(ebRecHitToken_, ebrechitHandle); if (!ebrechitHandle.isValid()) { @@ -164,68 +159,68 @@ void RecHitTester::analyze(const edm::Event& iEvent, const edm::EventSetup& iSet auto pfrechits = *pfrechitHandle; for (auto h : ebsimhits) { - float eta = caloGeom.getPosition(h.id()).eta(); - float phi = caloGeom.getPosition(h.id()).phi(); + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); - h2d_ebsimHits_["En_Eta"]->Fill(h.energy(), eta); - h2d_ebsimHits_["En_Phi"]->Fill(h.energy(), phi); - h2d_ebsimHits_["Eta_Phi"]->Fill(eta, phi); + h2d_ebsimHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_ebsimHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_ebsimHits_["Eta_Phi"]->Fill(eta, phi); } for (auto h : eesimhits) { - float eta = caloGeom.getPosition(h.id()).eta(); - float phi = caloGeom.getPosition(h.id()).phi(); + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); - h2d_eesimHits_["En_Eta"]->Fill(h.energy(), eta); - h2d_eesimHits_["En_Phi"]->Fill(h.energy(), phi); - h2d_eesimHits_["Eta_Phi"]->Fill(eta, phi); + h2d_eesimHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_eesimHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_eesimHits_["Eta_Phi"]->Fill(eta, phi); } for (auto h : ebuncalibrechits) { - float eta = caloGeom.getPosition(h.id()).eta(); - float phi = caloGeom.getPosition(h.id()).phi(); + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); - h2d_ebuncalibRecHits_["En_Eta"]->Fill(h.amplitude(), eta); - h2d_ebuncalibRecHits_["En_Phi"]->Fill(h.amplitude(), phi); - h2d_ebuncalibRecHits_["Eta_Phi"]->Fill(eta, phi); + h2d_ebuncalibRecHits_["En_Eta"]->Fill(h.amplitude(), eta); + h2d_ebuncalibRecHits_["En_Phi"]->Fill(h.amplitude(), phi); + h2d_ebuncalibRecHits_["Eta_Phi"]->Fill(eta, phi); } for (auto h : eeuncalibrechits) { - float eta = caloGeom.getPosition(h.id()).eta(); - float phi = caloGeom.getPosition(h.id()).phi(); + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); - h2d_eeuncalibRecHits_["En_Eta"]->Fill(h.amplitude(), eta); - h2d_eeuncalibRecHits_["En_Phi"]->Fill(h.amplitude(), phi); - h2d_eeuncalibRecHits_["Eta_Phi"]->Fill(eta, phi); + h2d_eeuncalibRecHits_["En_Eta"]->Fill(h.amplitude(), eta); + h2d_eeuncalibRecHits_["En_Phi"]->Fill(h.amplitude(), phi); + h2d_eeuncalibRecHits_["Eta_Phi"]->Fill(eta, phi); } - + for (auto h : ebrechits) { - float eta = caloGeom.getPosition(h.id()).eta(); - float phi = caloGeom.getPosition(h.id()).phi(); + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); - h2d_ebrecHits_["En_Eta"]->Fill(h.energy(), eta); - h2d_ebrecHits_["En_Phi"]->Fill(h.energy(), phi); - h2d_ebrecHits_["Eta_Phi"]->Fill(eta, phi); + h2d_ebrecHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_ebrecHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_ebrecHits_["Eta_Phi"]->Fill(eta, phi); } for (auto h : eerechits) { - float eta = caloGeom.getPosition(h.id()).eta(); - float phi = caloGeom.getPosition(h.id()).phi(); + float eta = caloGeom.getPosition(h.id()).eta(); + float phi = caloGeom.getPosition(h.id()).phi(); - h2d_eerecHits_["En_Eta"]->Fill(h.energy(), eta); - h2d_eerecHits_["En_Phi"]->Fill(h.energy(), phi); - h2d_eerecHits_["Eta_Phi"]->Fill(eta, phi); + h2d_eerecHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_eerecHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_eerecHits_["Eta_Phi"]->Fill(eta, phi); } - for (auto h : pfrechits) { - float eta = caloGeom.getPosition(h.detId()).eta(); - float phi = caloGeom.getPosition(h.detId()).phi(); + for (const auto& h : pfrechits) { + float eta = caloGeom.getPosition(h.detId()).eta(); + float phi = caloGeom.getPosition(h.detId()).phi(); - h2d_pfRecHits_["En_Eta"]->Fill(h.energy(), eta); - h2d_pfRecHits_["En_Phi"]->Fill(h.energy(), phi); - h2d_pfRecHits_["Eta_Phi"]->Fill(eta, phi); + h2d_pfRecHits_["En_Eta"]->Fill(h.energy(), eta); + h2d_pfRecHits_["En_Phi"]->Fill(h.energy(), phi); + h2d_pfRecHits_["Eta_Phi"]->Fill(eta, phi); } } void RecHitTester::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { - edm::ParameterSetDescription desc; + edm::ParameterSetDescription desc; desc.add("outFolder", "HLT/ParticleFlow"); desc.add("ebSimHits", edm::InputTag("g4SimHits", "EcalHitsEB")); desc.add("eeSimHits", edm::InputTag("g4SimHits", "EcalHitsEE")); From 9c8186f730053b87bd45fa1837ec2b7691792f4c Mon Sep 17 00:00:00 2001 From: Marco Rovere Date: Fri, 12 Dec 2025 15:24:43 +0100 Subject: [PATCH 32/64] Order hits and fraction by det, subdet and rawId. --- .../CaloAnalysis/interface/SimCluster.h | 226 +++++++++++++++--- .../CaloAnalysis/src/classes_def.xml | 6 +- .../plugins/CaloTruthAccumulator.cc | 10 +- 3 files changed, 208 insertions(+), 34 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index feeef822aec4a..bbacacfe29389 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -1,6 +1,23 @@ #ifndef SimDataFormats_SimCluster_h #define SimDataFormats_SimCluster_h +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/HepMCCandidate/interface/GenParticle.h" #include "DataFormats/Math/interface/LorentzVector.h" #include "DataFormats/Math/interface/Point3D.h" @@ -10,22 +27,6 @@ #include "SimDataFormats/EncodedEventId/interface/EncodedEventId.h" #include "SimDataFormats/Track/interface/SimTrack.h" -#include -#include -#include -#include - -/** @brief Monte Carlo truth information used for calorimeter reco validation - * - * Object with copies to the original SimTrack, and eventual reference to GenParticle - * Simulated calorimeter hits are saved as a list of pairs (DetId, fraction of reco hit energy contributed by the SimTrack) - * ('absolute' hit energy is usually not saved) - * - * @author original author unknown, re-engineering and slimming by Subir Sarkar - * (subir.sarkar@cern.ch), some tweaking and documentation by Mark Grimes - * (mark.grimes@bristol.ac.uk). - * @date original date unknown, re-engineering Jan-May 2013 - */ class SimCluster { friend std::ostream &operator<<(std::ostream &s, SimCluster const &tp); @@ -40,10 +41,56 @@ class SimCluster { typedef reco::GenParticleRefVector::iterator genp_iterator; typedef std::vector::const_iterator g4t_iterator; - SimCluster() = default; + // Zero-copy hit+fraction view (iterates as pairs) + struct HitsAndFractionsView { + std::span hits; + std::span fractions; + + struct iterator { + using iterator_category = std::random_access_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using reference = value_type; // returned by value + using pointer = void; + + const uint32_t* h = nullptr; + const float* f = nullptr; + + reference operator*() const { return {*h, *f}; } + + iterator& operator++() { ++h; ++f; return *this; } + iterator operator++(int) { auto tmp = *this; ++(*this); return tmp; } + iterator& operator--() { --h; --f; return *this; } + iterator operator--(int) { auto tmp = *this; --(*this); return tmp; } + + iterator& operator+=(difference_type n) { h += n; f += n; return *this; } + iterator& operator-=(difference_type n) { return (*this) += (-n); } + friend iterator operator+(iterator it, difference_type n) { return it += n; } + friend iterator operator-(iterator it, difference_type n) { return it -= n; } + friend difference_type operator-(iterator a, iterator b) { return a.h - b.h; } + + friend bool operator==(iterator a, iterator b) { return a.h == b.h; } + friend bool operator!=(iterator a, iterator b) { return !(a == b); } + friend bool operator<(iterator a, iterator b) { return a.h < b.h; } + }; + + iterator begin() const { return iterator{hits.data(), fractions.data()}; } + iterator end() const { + return iterator{hits.data() + static_cast(hits.size()), + fractions.data() + static_cast(fractions.size())}; + } + + size_t size() const { return hits.size(); } + bool empty() const { return hits.empty(); } + }; + + SimCluster() = default; SimCluster(const SimTrack &simtrk); SimCluster(EncodedEventId eventID, uint32_t particleID); // for PU + + ~SimCluster() = default; + /** Build a SimCluster from a collection of SimCluster. Hits&fractions are merged, SimTracks are all added in g4Tracks_ */ template requires std::ranges::input_range && std::same_as, SimCluster> @@ -176,13 +223,18 @@ class SimCluster { void addRecHitAndFraction(uint32_t hit, float fraction) { hits_.emplace_back(hit); fractions_.emplace_back(fraction); + hitsFinalized_ = false; } /** @brief add rechit energy */ - void addHitEnergy(float energy) { energies_.emplace_back(energy); } + void addHitEnergy(float energy) { + energies_.emplace_back(energy); + hitsFinalized_ = false; + } - /** @brief Returns list of rechit IDs and fractions for this SimCluster */ + /** @brief Returns list of rechit IDs and fractions for this SimCluster (copying legacy API) */ std::vector> hits_and_fractions() const { + // legacy returns a copy; now deterministic because finalizeHits() sorted it std::vector> result; result.reserve(hits_.size()); for (size_t i = 0; i < hits_.size(); ++i) { @@ -191,38 +243,36 @@ class SimCluster { return result; } - /** @brief Returns filtered list of rechit IDs and fractions for this SimCluster based on a predicate */ + /** @brief Returns filtered list of rechit IDs and fractions for this SimCluster based on a predicate (copying) */ std::vector> filtered_hits_and_fractions( const std::function &predicate) const { std::vector> result; for (size_t i = 0; i < hits_.size(); ++i) { DetId detid(hits_[i]); - if (predicate(detid)) { - result.emplace_back(hits_[i], fractions_[i]); - } + if (predicate(detid)) result.emplace_back(hits_[i], fractions_[i]); } return result; } - /** @brief Returns list of rechit IDs and energies for this SimCluster */ + /** @brief Returns list of rechit IDs and energies for this SimCluster (copying legacy API) */ std::vector> hits_and_energies() const { assert(hits_.size() == energies_.size()); std::vector> result; result.reserve(hits_.size()); - for (size_t i = 0; i < hits_.size(); ++i) { - result.emplace_back(hits_[i], energies_[i]); - } + for (size_t i = 0; i < hits_.size(); ++i) result.emplace_back(hits_[i], energies_[i]); return result; } - /** @brief clear the hits and fractions list */ void clearHitsAndFractions() { std::vector().swap(hits_); std::vector().swap(fractions_); + hitsFinalized_ = false; } - /** @brief clear the energies list */ - void clearHitsEnergy() { std::vector().swap(energies_); } + void clearHitsEnergy() { + std::vector().swap(energies_); + hitsFinalized_ = false; + } /** @brief returns the accumulated sim energy in the cluster */ float simEnergy() const { return simhit_energy_; } @@ -233,6 +283,85 @@ class SimCluster { ++nsimhits_; } + // -------------------------------------------------------------------------- + // New: producer-side "finalization" (to be called before putting in the event) + // -------------------------------------------------------------------------- + void finalizeHits() { + // Keep your original implicit invariant: + assert(hits_.size() == fractions_.size()); + // Energies are optional but if present must align. + if (!energies_.empty()) assert(energies_.size() == hits_.size()); + + // Already finalized? keep it cheap and idempotent. + if (hitsFinalized_) return; + + // Sort by (det, subdet, rawid) + std::vector order(hits_.size()); + std::iota(order.begin(), order.end(), 0); + + auto key = [&](size_t i) { + DetId id(hits_[i]); + return std::tuple(static_cast(id.det()), id.subdetId(), hits_[i]); + }; + + std::sort(order.begin(), order.end(), [&](size_t a, size_t b) { return key(a) < key(b); }); + + applyPermutation_(hits_, order); + applyPermutation_(fractions_, order); + if (!energies_.empty()) applyPermutation_(energies_, order); + + buildDetRanges_(); + + hitsFinalized_ = true; + } + + // -------------------------------------------------------------------------- + // cost-free views (require finalizeHits has been called) + // -------------------------------------------------------------------------- + HitsAndFractionsView hits_and_fractions_view() const { + assertFinalized_(); + return HitsAndFractionsView{std::span(hits_.data(), hits_.size()), + std::span(fractions_.data(), fractions_.size())}; + } + + HitsAndFractionsView hits_and_fractions_view(DetId::Detector det) const { + assertFinalized_(); + const auto idx = detIndex_(det); + auto [b, e] = detRanges_[idx]; + return HitsAndFractionsView{std::span(hits_.data() + b, e - b), + std::span(fractions_.data() + b, e - b)}; + } + + // det + subdet (still zero-copy; uses binary search within the det block) + HitsAndFractionsView hits_and_fractions_view(DetId::Detector det, int subdetId) const { + assertFinalized_(); + const auto idx = detIndex_(det); + auto [b, e] = detRanges_[idx]; + if (b == e) return HitsAndFractionsView{}; + + auto beginIt = hits_.begin() + static_cast(b); + auto endIt = hits_.begin() + static_cast(e); + + auto keyOf = [](uint32_t rawid) { + DetId id(rawid); + return std::pair(id.subdetId(), rawid); + }; + + const auto lowKey = std::pair(subdetId, 0u); + const auto highKey = std::pair(subdetId, std::numeric_limits::max()); + + auto lo = std::lower_bound(beginIt, endIt, lowKey, + [&](uint32_t rawid, const auto &k) { return keyOf(rawid) < k; }); + auto hi = std::upper_bound(beginIt, endIt, highKey, + [&](const auto &k, uint32_t rawid) { return k < keyOf(rawid); }); + + const size_t bb = static_cast(std::distance(hits_.begin(), lo)); + const size_t ee = static_cast(std::distance(hits_.begin(), hi)); + + return HitsAndFractionsView{std::span(hits_.data() + bb, ee - bb), + std::span(fractions_.data() + bb, ee - bb)}; + } + protected: uint64_t nsimhits_{0}; EncodedEventId event_; @@ -248,6 +377,43 @@ class SimCluster { std::vector g4Tracks_; ///< Copies of SimTrack used to build SimCluster (usually there is only one) /// Ref to GenParticle (in case the SimCluster is created from the entire GenParticle). Usually either empty or length 1 reco::GenParticleRefVector genParticles_; + +private: + static constexpr size_t kMaxDetectors_ = 32; // Probably 16 could be enough + + bool hitsFinalized_{false}; + std::array, kMaxDetectors_> detRanges_{}; // [begin,end) per detector + + static size_t detIndex_(DetId::Detector det) { + const auto idx = static_cast(det); + assert(idx < kMaxDetectors_); + return idx; + } + + void assertFinalized_() const { + assert(hitsFinalized_ && "SimCluster: hits not finalized. Call finalizeHits() in the producer before persisting."); + assert(hits_.size() == fractions_.size()); + } + + void buildDetRanges_() { + detRanges_.fill({0u, 0u}); + size_t i = 0; + while (i < hits_.size()) { + DetId id(hits_[i]); + const auto idx = detIndex_(static_cast(id.det())); + const size_t begin = i; + do { ++i; } while (i < hits_.size() && DetId(hits_[i]).det() == id.det()); + detRanges_[idx] = {begin, i}; + } + } + + template + static void applyPermutation_(std::vector &v, const std::vector &order) { + std::vector tmp; + tmp.reserve(v.size()); + for (size_t idx : order) tmp.push_back(v[idx]); + v.swap(tmp); + } }; template diff --git a/SimDataFormats/CaloAnalysis/src/classes_def.xml b/SimDataFormats/CaloAnalysis/src/classes_def.xml index 5e5bcc21cecc3..5dd6992a11cbf 100644 --- a/SimDataFormats/CaloAnalysis/src/classes_def.xml +++ b/SimDataFormats/CaloAnalysis/src/classes_def.xml @@ -21,7 +21,8 @@ - + + @@ -38,7 +39,8 @@ - + + diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index 02661769fa415..d9bf1204c0af9 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -697,14 +697,14 @@ void CaloTruthAccumulator::accumulate(PileUpEventPrincipal const &event, } namespace { - /** Normalize the energies in the SimCluster/CaloParticle collection, from absolute SimHit energies to fraction of total simHits energies */ + /** Normalize the energies in the SimCluster/CaloParticle collection, from absolute SimHit energies to fraction of total simHits energies */ template void normalizeCollection(SimCaloCollection &simClusters, std::unordered_map const &detIdToTotalSimEnergy) { for (auto &sc : simClusters) { auto hitsAndEnergies = sc.hits_and_fractions(); sc.clearHitsAndFractions(); - // Note : addHitEnergy is actually never used, so we do not clear and refill for it + sc.clearHitsEnergy(); for (auto &hAndE : hitsAndEnergies) { const float totalenergy = detIdToTotalSimEnergy.at(hAndE.first); float fraction = 0.; @@ -714,7 +714,9 @@ namespace { edm::LogWarning("CaloTruthAccumulator") << "TotalSimEnergy for hit " << hAndE.first << " is 0! The fraction for this hit cannot be computed."; sc.addRecHitAndFraction(hAndE.first, fraction); + sc.addHitEnergy(hAndE.second); } + sc.finalizeHits(); } } }; // namespace @@ -735,6 +737,10 @@ void CaloTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup cons std::copy(m_detIdToTotalSimEnergy.begin(), m_detIdToTotalSimEnergy.end(), std::back_inserter(*totalEnergies)); std::sort(totalEnergies->begin(), totalEnergies->end()); event.put(std::move(totalEnergies), "MergedCaloTruth"); + // make sure persisted SimClusters have sorted hits and built ranges + for (auto &sc : *(output_.pSimClusters)) { + sc.finalizeHits(); + } } else { applyToSimClusterConfig( [this](auto &config) { normalizeCollection(config.outputClusters, m_detIdToTotalSimEnergy); }); From 5d280a6199331a4060012bab2d29a9269725825d Mon Sep 17 00:00:00 2001 From: Marco Musich Date: Wed, 17 Dec 2025 09:24:54 +0100 Subject: [PATCH 33/64] Order hits and fraction by det in range min and max. --- .../CaloAnalysis/interface/SimCluster.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index bbacacfe29389..b38527c36e62d 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -362,6 +362,25 @@ class SimCluster { std::span(fractions_.data() + bb, ee - bb)}; } + // min, max detids + HitsAndFractionsView hits_and_fractions_view(DetId::Detector detIdMin, DetId::Detector detIdMax) const { + assertFinalized_(); + + // Find the first and last indices using the same ranges used + // for the per-detector lookup. + const auto idxMin = detIndex_(detIdMin); + const auto idxMax = detIndex_(detIdMax); + + const auto [bMin, eMin] = detRanges_[idxMin]; + const auto [bMax, eMax] = detRanges_[idxMax]; + + const uint32_t begin = bMin; // inclusive + const uint32_t end = eMax; // exclusive + + return HitsAndFractionsView{std::span(hits_.data() + begin, end - begin), + std::span(fractions_.data() + begin, end - begin)}; + } + protected: uint64_t nsimhits_{0}; EncodedEventId event_; From 5d0b9fb3fa7ccfa53d5c4583277c64038c342245 Mon Sep 17 00:00:00 2001 From: Elena Vernazza Date: Wed, 17 Dec 2025 15:15:11 +0100 Subject: [PATCH 34/64] Option2: Use configurable hits_and_fractions_view in LCToCP and LSToSC associators Co-authored-by: Marco Musich --- .../LCToCPAssociatorByEnergyScoreImpl.cc | 43 +++++----- .../LCToCPAssociatorByEnergyScoreImpl.h | 8 +- .../plugins/LCToCPAssociatorEDProducer.cc | 16 +++- .../LCToSCAssociatorByEnergyScoreImpl.cc | 43 +++++----- .../LCToSCAssociatorByEnergyScoreImpl.h | 8 +- .../plugins/LCToSCAssociatorEDProducer.cc | 16 +++- .../LayerClusterToCaloParticleAssociator.h | 10 ++- ...rClusterToCaloParticleAssociatorBaseImpl.h | 5 +- .../LayerClusterToSimClusterAssociator.h | 8 +- ...yerClusterToSimClusterAssociatorBaseImpl.h | 5 +- ...ClusterToCaloParticleAssociatorBaseImpl.cc | 4 +- ...erClusterToSimClusterAssociatorBaseImpl.cc | 4 +- .../python/caloTruthProducer_cfi.py | 86 ++++++++++++------- .../python/hltHGCalSimValid_cff.py | 6 +- .../python/hltPFValidation_cfi.py | 6 +- 15 files changed, 163 insertions(+), 105 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc index 89b53904c6b5b..a357102fa9b4e 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc @@ -33,7 +33,7 @@ LCToCPAssociatorByEnergyScoreImplT::LCToCPAssociatorByEnergyScoreI template ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnections( - const edm::Handle& cCCH, const edm::Handle& cPCH) const { + const edm::Handle& cCCH, const edm::Handle& cPCH, const std::vector& detIds) const { // Get collections const auto& clusters = *cCCH.product(); const auto& caloParticles = *cPCH.product(); @@ -75,15 +75,16 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti const SimClusterRefVector& simClusterRefVector = caloParticles[cpId].simClusters(); for (const auto& it_sc : simClusterRefVector) { const SimCluster& simCluster = (*(it_sc)); - std::vector> hits_and_fractions; - if constexpr (std::is_same_v) - hits_and_fractions = simCluster.filtered_hits_and_fractions( - [this](const DetId& detid) { return !recHitTools_->isBarrel(detid); }); - else - hits_and_fractions = simCluster.filtered_hits_and_fractions( - [this](const DetId& detid) { return recHitTools_->isBarrel(detid); }); - for (const auto& it_haf : hits_and_fractions) { - const auto hitid = (it_haf.first); + + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simCluster.hits_and_fractions_view() : + (detIds.size() == 1 ? simCluster.hits_and_fractions_view(detIds[0]) : + simCluster.hits_and_fractions_view(*detIds.begin(), *(detIds.rbegin()))); + + for (size_t i = 0; i < hafView.hits.size(); ++i) { + const uint32_t hitid = hafView.hits[i]; + const float fraction = hafView.fractions[i]; + unsigned int cpLayerId = recHitTools_->getLayerWithOffset(hitid); if constexpr (std::is_same_v) cpLayerId += layers_ * ((recHitTools_->zside(hitid) + 1) >> 1) - 1; @@ -94,19 +95,19 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti auto hit_find_it = detIdToCaloParticleId_Map.find(hitid); if (hit_find_it == detIdToCaloParticleId_Map.end()) { detIdToCaloParticleId_Map[hitid] = std::vector(); - detIdToCaloParticleId_Map[hitid].emplace_back(cpId, it_haf.second); + detIdToCaloParticleId_Map[hitid].emplace_back(cpId, fraction); } else { auto findHitIt = std::find(detIdToCaloParticleId_Map[hitid].begin(), detIdToCaloParticleId_Map[hitid].end(), - ticl::detIdInfoInCluster{cpId, it_haf.second}); + ticl::detIdInfoInCluster{cpId, fraction}); if (findHitIt != detIdToCaloParticleId_Map[hitid].end()) { - findHitIt->fraction += it_haf.second; + findHitIt->fraction += fraction; } else { - detIdToCaloParticleId_Map[hitid].emplace_back(cpId, it_haf.second); + detIdToCaloParticleId_Map[hitid].emplace_back(cpId, fraction); } } const HIT* hit = &hitsMS[itcheck->second]; - cPOnLayer[cpId][cpLayerId].energy += it_haf.second * hit->energy(); + cPOnLayer[cpId][cpLayerId].energy += fraction * hit->energy(); // We need to compress the hits and fractions in order to have a // reasonable score between CP and LC. Imagine, for example, that a // CP has detID X used by 2 SimClusters with different fractions. If @@ -117,9 +118,9 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti auto found = std::find_if( std::begin(haf), std::end(haf), [&hitid](const std::pair& v) { return v.first == hitid; }); if (found != haf.end()) { - found->second += it_haf.second; + found->second += fraction; } else { - cPOnLayer[cpId][cpLayerId].hits_and_fractions.emplace_back(hitid, it_haf.second); + cPOnLayer[cpId][cpLayerId].hits_and_fractions.emplace_back(hitid, fraction); } } } @@ -542,14 +543,14 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti template ticl::RecoToSimCollectionT LCToCPAssociatorByEnergyScoreImplT::associateRecoToSim( - const edm::Handle& cCCH, const edm::Handle& cPCH) const { + const edm::Handle& cCCH, const edm::Handle& cPCH, const std::vector& detIds) const { ticl::RecoToSimCollectionT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { edm::LogWarning("LCToCPAssociatorByEnergyScoreImplT") << "hitMap_ is null or empty, skipping association."; return returnValue; // return empty collection } - const auto& links = makeConnections(cCCH, cPCH); + const auto& links = makeConnections(cCCH, cPCH, detIds); const auto& cpsInLayerCluster = std::get<0>(links); for (size_t lcId = 0; lcId < cpsInLayerCluster.size(); ++lcId) { @@ -568,7 +569,7 @@ ticl::RecoToSimCollectionT LCToCPAssociatorByEnergyScoreImplT ticl::SimToRecoCollectionT LCToCPAssociatorByEnergyScoreImplT::associateSimToReco( - const edm::Handle& cCCH, const edm::Handle& cPCH) const { + const edm::Handle& cCCH, const edm::Handle& cPCH, const std::vector& detIds) const { ticl::SimToRecoCollectionT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { @@ -576,7 +577,7 @@ ticl::SimToRecoCollectionT LCToCPAssociatorByEnergyScoreImplT(links); for (size_t cpId = 0; cpId < cPOnLayer.size(); ++cpId) { for (size_t layerId = 0; layerId < cPOnLayer[cpId].size(); ++layerId) { diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h index cf95996c8ce3a..dd5420880a60c 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h @@ -5,6 +5,7 @@ #include #include // shared_ptr +#include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/ForwardDetId/interface/HGCalDetId.h" #include "DataFormats/HGCRecHit/interface/HGCRecHit.h" #include "DataFormats/HGCRecHit/interface/HGCRecHitCollections.h" @@ -77,10 +78,10 @@ class LCToCPAssociatorByEnergyScoreImplT : public ticl::LayerClusterToCaloPartic const multiCollectionT &hits); ticl::RecoToSimCollectionT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &cPCH) const override; + const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const override; ticl::SimToRecoCollectionT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &cPCH) const override; + const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const override; private: const bool hardScatterOnly_; @@ -89,7 +90,8 @@ class LCToCPAssociatorByEnergyScoreImplT : public ticl::LayerClusterToCaloPartic unsigned layers_; edm::EDProductGetter const *productGetter_; ticl::association makeConnections(const edm::Handle &cCH, - const edm::Handle &cPCH) const; + const edm::Handle &cPCH, + const std::vector &detIds) const; multiCollectionT hits_; }; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc index 1ab593840f0ca..39e072e05342e 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc @@ -10,8 +10,10 @@ // user include files #include "FWCore/Framework/interface/global/EDProducer.h" +#include "DataFormats/DetId/interface/DetId.h" #include "FWCore/Framework/interface/Event.h" #include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/allowedValues.h" #include "FWCore/Framework/interface/ESHandle.h" #include "FWCore/ParameterSet/interface/ParameterSet.h" #include "SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h" @@ -45,10 +47,18 @@ class LCToCPAssociatorEDProducerT : public edm::global::EDProducer<> { void produce(edm::StreamID, edm::Event &, const edm::EventSetup &) const override; edm::InputTag label_lc; + std::string filter_sim_hits_; edm::EDGetTokenT CPCollectionToken_; edm::EDGetTokenT LCCollectionToken_; edm::EDGetTokenT> associatorToken_; + + const std::unordered_map> DetIdMap = { + {"", std::vector{}}, + {"Ecal", std::vector{DetId::Ecal}}, + {"Hcal", std::vector{DetId::Hcal}}, + {"HGCal", std::vector{DetId::HGCalEE, DetId::HGCalHSc}} + }; }; template @@ -57,6 +67,7 @@ LCToCPAssociatorEDProducerT::LCToCPAssociatorEDProducerT(const edm::Par produces>(); label_lc = pset.getParameter("label_lc"); + filter_sim_hits_ = pset.getParameter("filter_sim_hits"); CPCollectionToken_ = consumes(pset.getParameter("label_cp")); LCCollectionToken_ = consumes(label_lc); @@ -109,10 +120,10 @@ void LCToCPAssociatorEDProducerT::produce(edm::StreamID, // associate LC and CP LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; - ticl::RecoToSimCollectionT recSimColl = theAssociator->associateRecoToSim(LCCollection, CPCollection); + ticl::RecoToSimCollectionT recSimColl = theAssociator->associateRecoToSim(LCCollection, CPCollection, DetIdMap.at(filter_sim_hits_)); LogTrace("AssociatorValidator") << "Calling associateSimToReco method\n"; - ticl::SimToRecoCollectionT simRecColl = theAssociator->associateSimToReco(LCCollection, CPCollection); + ticl::SimToRecoCollectionT simRecColl = theAssociator->associateSimToReco(LCCollection, CPCollection, DetIdMap.at(filter_sim_hits_)); auto rts = std::make_unique>(recSimColl); auto str = std::make_unique>(simRecColl); @@ -125,6 +136,7 @@ template void LCToCPAssociatorEDProducerT::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { edm::ParameterSetDescription desc; desc.add("label_cp", edm::InputTag("mix", "MergedCaloTruth")); + desc.ifValue(edm::ParameterDescription("filter_sim_hits", "", true), edm::allowedValues("", "Ecal", "Hcal", "HGCal")); desc.add("label_lc", edm::InputTag("hgcalMergeLayerClusters")); desc.add("associator", edm::InputTag("lcAssocByEnergyScoreProducer")); descriptions.addWithDefaultLabel(desc); diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index c2be3df503681..952e7c136d2a7 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -25,7 +25,7 @@ LCToSCAssociatorByEnergyScoreImplT::LCToSCAssociatorByEnergyScoreI template ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnections( - const edm::Handle& cCCH, const edm::Handle& sCCH) const { + const edm::Handle& cCCH, const edm::Handle& sCCH, const std::vector& detIds) const { // Get collections const auto& clusters = *cCCH.product(); const auto& simClusters = *sCCH.product(); @@ -74,29 +74,28 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti edm::MultiSpan hitsMS(hits_); for (const auto& scId : sCIndices) { - std::vector> hits_and_fractions = simClusters[scId].hits_and_fractions(); - if constexpr (std::is_same_v) - hits_and_fractions = simClusters[scId].filtered_hits_and_fractions( - [this](const DetId& detid) { return !recHitTools_->isBarrel(detid); }); - else { - hits_and_fractions = simClusters[scId].filtered_hits_and_fractions( - [this](const DetId& detid) { return recHitTools_->isBarrel(detid); }); - } - for (const auto& it_haf : hits_and_fractions) { - const auto hitid = (it_haf.first); + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simClusters[scId].hits_and_fractions_view() : + (detIds.size() == 1 ? simClusters[scId].hits_and_fractions_view(detIds[0]) : + simClusters[scId].hits_and_fractions_view(*detIds.begin(), *(detIds.rbegin()))); + + for (size_t i = 0; i < hafView.hits.size(); ++i) { + const uint32_t hitid = hafView.hits[i]; + const float fraction = hafView.fractions[i]; + unsigned int scLayerId = recHitTools_->getLayer(hitid); + if constexpr (std::is_same_v) scLayerId += layers_ * ((recHitTools_->zside(hitid) + 1) >> 1) - 1; + const auto itcheck = hitMap_->find(hitid); if (itcheck != hitMap_->end()) { - auto hit_find_it = detIdToSimClusterId_Map.find(hitid); - if (hit_find_it == detIdToSimClusterId_Map.end()) { - detIdToSimClusterId_Map[hitid] = std::vector(); - } - detIdToSimClusterId_Map[hitid].emplace_back(scId, it_haf.second); + auto& vec = detIdToSimClusterId_Map[hitid]; + vec.emplace_back(scId, fraction); + const HIT* hit = &hitsMS[itcheck->second]; - lcsInSimCluster[scId][scLayerId].energy += it_haf.second * hit->energy(); - lcsInSimCluster[scId][scLayerId].hits_and_fractions.emplace_back(hitid, it_haf.second); + lcsInSimCluster[scId][scLayerId].energy += fraction * hit->energy(); + lcsInSimCluster[scId][scLayerId].hits_and_fractions.emplace_back(hitid, fraction); } } } // end of loop over SimClusters @@ -557,14 +556,14 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti template ticl::RecoToSimCollectionWithSimClustersT LCToSCAssociatorByEnergyScoreImplT::associateRecoToSim( - const edm::Handle& cCCH, const edm::Handle& sCCH) const { + const edm::Handle& cCCH, const edm::Handle& sCCH, const std::vector& detIds) const { ticl::RecoToSimCollectionWithSimClustersT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { edm::LogWarning("LCToSCAssociatorByEnergyScoreImplT") << "hitMap_ is null or empty, skipping association."; return returnValue; // return empty collection } - const auto& links = makeConnections(cCCH, sCCH); + const auto& links = makeConnections(cCCH, sCCH, detIds); const auto& scsInLayerCluster = std::get<0>(links); for (size_t lcId = 0; lcId < scsInLayerCluster.size(); ++lcId) { @@ -583,7 +582,7 @@ ticl::RecoToSimCollectionWithSimClustersT LCToSCAssociatorByEnergyScore template ticl::SimToRecoCollectionWithSimClustersT LCToSCAssociatorByEnergyScoreImplT::associateSimToReco( - const edm::Handle& cCCH, const edm::Handle& sCCH) const { + const edm::Handle& cCCH, const edm::Handle& sCCH, const std::vector& detIds) const { ticl::SimToRecoCollectionWithSimClustersT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { @@ -591,7 +590,7 @@ ticl::SimToRecoCollectionWithSimClustersT LCToSCAssociatorByEnergyScore return returnValue; // return empty collection } - const auto& links = makeConnections(cCCH, sCCH); + const auto& links = makeConnections(cCCH, sCCH, detIds); const auto& lcsInSimCluster = std::get<1>(links); for (size_t scId = 0; scId < lcsInSimCluster.size(); ++scId) { for (size_t layerId = 0; layerId < lcsInSimCluster[scId].size(); ++layerId) { diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h index ed517e68599c8..c29bb34b95f69 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h @@ -5,6 +5,7 @@ #include #include // shared_ptr +#include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/ForwardDetId/interface/HGCalDetId.h" #include "DataFormats/HGCRecHit/interface/HGCRecHit.h" #include "DataFormats/HGCRecHit/interface/HGCRecHitCollections.h" @@ -74,10 +75,10 @@ class LCToSCAssociatorByEnergyScoreImplT : public ticl::LayerClusterToSimCluster const multiCollectionT &hits); ticl::RecoToSimCollectionWithSimClustersT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &sCCH) const override; + const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const override; ticl::SimToRecoCollectionWithSimClustersT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &sCCH) const override; + const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const override; private: const bool hardScatterOnly_; @@ -86,7 +87,8 @@ class LCToSCAssociatorByEnergyScoreImplT : public ticl::LayerClusterToSimCluster unsigned layers_; edm::EDProductGetter const *productGetter_; ticl::association makeConnections(const edm::Handle &cCH, - const edm::Handle &sCCH) const; + const edm::Handle &sCCH, + const std::vector &detIds) const; multiCollectionT hits_; }; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index 98529bcfd129b..8a01acf904ea9 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -11,9 +11,11 @@ // user include files #include "DataFormats/CaloRecHit/interface/CaloClusterFwd.h" #include "DataFormats/ParticleFlowReco/interface/PFClusterFwd.h" +#include "DataFormats/DetId/interface/DetId.h" #include "FWCore/Framework/interface/ESHandle.h" #include "FWCore/Framework/interface/Event.h" #include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/ParameterSet/interface/allowedValues.h" #include "FWCore/Framework/interface/global/EDProducer.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" @@ -38,10 +40,18 @@ class LCToSCAssociatorEDProducerT : public edm::global::EDProducer<> { edm::InputTag label_lcl; edm::InputTag label_scl; + std::string filter_sim_hits_; edm::EDGetTokenT SCCollectionToken_; edm::EDGetTokenT LCCollectionToken_; edm::EDGetTokenT> associatorToken_; + + const std::unordered_map> DetIdMap = { + {"", std::vector{}}, + {"Ecal", std::vector{DetId::Ecal}}, + {"Hcal", std::vector{DetId::Hcal}}, + {"HGCal", std::vector{DetId::HGCalEE, DetId::HGCalHSc}} + }; }; template @@ -51,6 +61,7 @@ LCToSCAssociatorEDProducerT::LCToSCAssociatorEDProducerT(const edm::Par label_lcl = pset.getParameter("label_lcl"); label_scl = pset.getParameter("label_scl"); + filter_sim_hits_ = pset.getParameter("filter_sim_hits"); LCCollectionToken_ = consumes(label_lcl); SCCollectionToken_ = consumes(label_scl); @@ -111,11 +122,11 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, // associate LC and SC LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; ticl::RecoToSimCollectionWithSimClustersT recSimColl = - theAssociator->associateRecoToSim(LCCollection, SCCollection); + theAssociator->associateRecoToSim(LCCollection, SCCollection, DetIdMap.at(filter_sim_hits_)); LogTrace("AssociatorValidator") << "Calling associateSimToReco method\n"; ticl::SimToRecoCollectionWithSimClustersT simRecColl = - theAssociator->associateSimToReco(LCCollection, SCCollection); + theAssociator->associateSimToReco(LCCollection, SCCollection, DetIdMap.at(filter_sim_hits_)); auto rts = std::make_unique>(recSimColl); auto str = std::make_unique>(simRecColl); @@ -128,6 +139,7 @@ template void LCToSCAssociatorEDProducerT::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { edm::ParameterSetDescription desc; desc.add("label_scl", edm::InputTag("mix", "MergedCaloTruth")); + desc.ifValue(edm::ParameterDescription("filter_sim_hits", "", true), edm::allowedValues("", "Ecal", "Hcal", "HGCal")); desc.add("label_lcl", edm::InputTag("hgcalMergeLayerClusters")); desc.add("associator", edm::InputTag("scAssocByEnergyScoreProducer")); descriptions.addWithDefaultLabel(desc); diff --git a/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h b/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h index 0e1432c26e40a..39ef428601353 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h +++ b/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h @@ -33,14 +33,16 @@ namespace ticl { // ---------- const member functions --------------------- /// Associate a LayerCluster to CaloParticles ticl::RecoToSimCollectionT associateRecoToSim(const edm::Handle &cCCH, - const edm::Handle &cPCH) const { - return m_impl->associateRecoToSim(cCCH, cPCH); + const edm::Handle &cPCH, + const std::vector &detIds) const { + return m_impl->associateRecoToSim(cCCH, cPCH, detIds); }; /// Associate a CaloParticle to LayerClusters ticl::SimToRecoCollectionT associateSimToReco(const edm::Handle &cCCH, - const edm::Handle &cPCH) const { - return m_impl->associateSimToReco(cCCH, cPCH); + const edm::Handle &cPCH, + const std::vector &detIds) const { + return m_impl->associateSimToReco(cCCH, cPCH, detIds); } private: diff --git a/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h b/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h index e7c2f975e2788..3af69a8997222 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h +++ b/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h @@ -10,6 +10,7 @@ * \author Marco Rovere */ +#include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/Common/interface/Handle.h" #include "DataFormats/Common/interface/AssociationMap.h" #include "DataFormats/CaloRecHit/interface/CaloClusterCollection.h" @@ -35,11 +36,11 @@ namespace ticl { /// Associate a LayerCluster to CaloParticles virtual ticl::RecoToSimCollectionT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &cPCH) const; + const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const; /// Associate a CaloParticle to LayerClusters virtual ticl::SimToRecoCollectionT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &cPCH) const; + const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const; }; } // namespace ticl diff --git a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h index a1bbce359ded5..f7ae1a44053d4 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h +++ b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h @@ -31,14 +31,14 @@ namespace ticl { // ---------- const member functions --------------------- /// Associate a LayerCluster to SimClusters RecoToSimCollectionWithSimClustersT associateRecoToSim( - const edm::Handle &cCCH, const edm::Handle &sCCH) const { - return m_impl->associateRecoToSim(cCCH, sCCH); + const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { + return m_impl->associateRecoToSim(cCCH, sCCH, detIds); }; /// Associate a SimCluster to LayerClusters SimToRecoCollectionWithSimClustersT associateSimToReco( - const edm::Handle &cCCH, const edm::Handle &sCCH) const { - return m_impl->associateSimToReco(cCCH, sCCH); + const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { + return m_impl->associateSimToReco(cCCH, sCCH, detIds); } private: diff --git a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h index effb0ac502f09..229b9ff2548f0 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h +++ b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h @@ -10,6 +10,7 @@ * \author Marco Rovere */ +#include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/Common/interface/Handle.h" #include "DataFormats/Common/interface/AssociationMap.h" #include "DataFormats/CaloRecHit/interface/CaloClusterCollection.h" @@ -35,11 +36,11 @@ namespace ticl { /// Associate a LayerCluster to SimClusters virtual RecoToSimCollectionWithSimClustersT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &sCCH) const; + const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const; /// Associate a SimCluster to LayerClusters virtual SimToRecoCollectionWithSimClustersT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &sCCH) const; + const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const; }; } // namespace ticl diff --git a/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc b/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc index 49039362fccba..b2b29c500e16a 100644 --- a/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc +++ b/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc @@ -10,13 +10,13 @@ namespace ticl { template ticl::RecoToSimCollectionT LayerClusterToCaloParticleAssociatorBaseImplT::associateRecoToSim( - const edm::Handle &cCCH, const edm::Handle &cPCH) const { + const edm::Handle &cCCH, const edm::Handle &cPCH, const std::vector &detIds) const { return ticl::RecoToSimCollectionT(); } template ticl::SimToRecoCollectionT LayerClusterToCaloParticleAssociatorBaseImplT::associateSimToReco( - const edm::Handle &cCCH, const edm::Handle &cPCH) const { + const edm::Handle &cCCH, const edm::Handle &cPCH, const std::vector &detIds) const { return ticl::SimToRecoCollectionT(); } } // namespace ticl diff --git a/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc b/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc index f16b48a0b5556..36137c392098d 100644 --- a/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc +++ b/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc @@ -10,13 +10,13 @@ namespace ticl { template RecoToSimCollectionWithSimClustersT LayerClusterToSimClusterAssociatorBaseImplT::associateRecoToSim( - const edm::Handle &cCCH, const edm::Handle &sCCH) const { + const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { return RecoToSimCollectionWithSimClustersT(); } template SimToRecoCollectionWithSimClustersT LayerClusterToSimClusterAssociatorBaseImplT::associateSimToReco( - const edm::Handle &cCCH, const edm::Handle &sCCH) const { + const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { return SimToRecoCollectionWithSimClustersT(); } diff --git a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py index 5969022364b0a..c38fc21b39a69 100644 --- a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py +++ b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py @@ -1,26 +1,48 @@ import FWCore.ParameterSet.Config as cms +run3_simHits = { + "ECAL": [ + cms.InputTag('g4SimHits', 'EcalHitsEB'), + cms.InputTag('g4SimHits', 'EcalHitsEE'), + ], + "HCAL": [ + cms.InputTag('g4SimHits', 'HcalHits'), + ], +} + +ph2_simHits = { + "ECAL": [ + cms.InputTag('g4SimHits', 'EcalHitsEB'), + ], + "HCAL": [ + cms.InputTag('g4SimHits', 'HcalHits'), + ], + "HGCAL": [ + cms.InputTag('g4SimHits', 'HGCHitsEE'), + cms.InputTag('g4SimHits', 'HGCHitsHEfront'), + cms.InputTag('g4SimHits', 'HGCHitsHEback'), + ] +} + +# Default run-3 configuration caloParticles = cms.PSet( accumulatorType = cms.string('CaloTruthAccumulator'), -# createUnmergedCollection = cms.bool(True), -# createMergedBremsstrahlung = cms.bool(True), -# createInitialVertexCollection = cms.bool(False), -# alwaysAddAncestors = cms.bool(True), - MinEnergy = cms.double(0.5), - MaxPseudoRapidity = cms.double(5.0), - produceLegacySimCluster = cms.bool(True), - produceBoundaryAndMergedSimCluster = cms.bool(True), - premixStage1 = cms.bool(False), - doHGCAL = cms.bool(False), + # createUnmergedCollection = cms.bool(True), + # createMergedBremsstrahlung = cms.bool(True), + # createInitialVertexCollection = cms.bool(False), + # alwaysAddAncestors = cms.bool(True), + MinEnergy = cms.double(0.5), + MaxPseudoRapidity = cms.double(5.0), + produceLegacySimCluster = cms.bool(True), + produceBoundaryAndMergedSimCluster = cms.bool(True), + premixStage1 = cms.bool(False), + doHGCAL = cms.bool(False), maximumPreviousBunchCrossing = cms.uint32(0), maximumSubsequentBunchCrossing = cms.uint32(0), simHitCollections = cms.PSet( - hgc = cms.VInputTag(), -# hcal = cms.VInputTag(cms.InputTag('g4SimHits','HcalHits')), - ecal = cms.VInputTag( - cms.InputTag('g4SimHits','EcalHitsEB'), - cms.InputTag('g4SimHits','EcalHitsEE'), - ) + hgc = cms.VInputTag(), + hcal = cms.VInputTag(*run3_simHits["HCAL"]), + ecal = cms.VInputTag(*run3_simHits["ECAL"]), ), simTrackCollection = cms.InputTag('g4SimHits'), simVertexCollection = cms.InputTag('g4SimHits'), @@ -32,11 +54,16 @@ ) ) +# Phase-2 configuration (HGCAL only) # [FIXME: with the isBarrel check in associators it could contain all hits] from Configuration.Eras.Modifier_phase2_common_cff import phase2_common -phase2_common.toModify(caloParticles, doHGCAL=True) - -from Configuration.ProcessModifiers.premix_stage1_cff import premix_stage1 -premix_stage1.toModify(caloParticles, premixStage1 = True) +phase2_common.toModify(caloParticles, + doHGCAL=True, + simHitCollections = cms.PSet( + hgc = cms.VInputTag(*ph2_simHits["HGCAL"]), + hcal = cms.VInputTag(*ph2_simHits["HCAL"]), + ecal = cms.VInputTag(*ph2_simHits["ECAL"]), + ), +) from Configuration.Eras.Modifier_phase2_hfnose_cff import phase2_hfnose phase2_hfnose.toModify( @@ -59,23 +86,18 @@ ) ) -from Configuration.Eras.Modifier_fastSim_cff import fastSim -fastSim.toReplaceWith(caloParticles, cms.PSet()) # don't allow this to run in fastsim - from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel ticl_barrel.toModify( caloParticles, simHitCollections = cms.PSet( - # hgc = cms.VInputTag( - # cms.InputTag('g4SimHits', 'HGCHitsEE'), - # cms.InputTag('g4SimHits', 'HGCHitsHEfront'), - # cms.InputTag('g4SimHits', 'HGCHitsHEback'), - # ), - # hcal = cms.VInputTag(cms.InputTag('g4SimHits', 'HcalHits')), - ecal = cms.VInputTag( - cms.InputTag('g4SimHits', 'EcalHitsEB') - ) + hgc = cms.VInputTag(*ph2_simHits["HGCAL"]), + hcal = cms.VInputTag(*ph2_simHits["HCAL"]), + ecal = cms.VInputTag(*ph2_simHits["ECAL"]), ) ) +from Configuration.Eras.Modifier_fastSim_cff import fastSim +fastSim.toReplaceWith(caloParticles, cms.PSet()) # don't allow this to run in fastsim +from Configuration.ProcessModifiers.premix_stage1_cff import premix_stage1 +premix_stage1.toModify(caloParticles, premixStage1 = True) diff --git a/Validation/Configuration/python/hltHGCalSimValid_cff.py b/Validation/Configuration/python/hltHGCalSimValid_cff.py index 6440afc962ca7..165524bf21106 100644 --- a/Validation/Configuration/python/hltHGCalSimValid_cff.py +++ b/Validation/Configuration/python/hltHGCalSimValid_cff.py @@ -81,12 +81,14 @@ hltBarrelLayerClusterCaloParticleAssociationProducer = _barrelLayerClusterCaloParticleAssociation.clone( associator = cms.InputTag("hltBarrelLcAssocByEnergyScoreProducer"), - label_lc = cms.InputTag("hltBarrelLayerClustersEB") + label_lc = cms.InputTag("hltBarrelLayerClustersEB"), + filter_sim_hits = cms.string("Ecal") ) hltBarrelLayerClusterSimClusterAssociationProducer = _barrelLayerClusterSimClusterAssociation.clone( associator = cms.InputTag("hltBarrelScAssocByEnergyScoreProducer"), - label_lcl = cms.InputTag("hltBarrelLayerClustersEB") + label_lcl = cms.InputTag("hltBarrelLayerClustersEB"), + filter_sim_hits = cms.string("Ecal") ) hltHgcalAndBarrelLayerClustersAssociatorsTask = cms.Task( diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index c5cc4965b2c60..2e3d1c6138750 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -9,7 +9,8 @@ hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), label_lcl = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - label_scl = cms.InputTag("mix","MergedCaloTruth") + label_scl = cms.InputTag("mix","MergedCaloTruth"), + filter_sim_hits = cms.string("Ecal") ) hltPFCpAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToCPAssociatorByEnergyScoreProducer", @@ -21,7 +22,8 @@ hltPFClusterCaloParticleAssociationProducerECAL = cms.EDProducer("PCToCPAssociatorEDProducer", associator = cms.InputTag("hltPFCpAssocByEnergyScoreProducer"), label_lc = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - label_cp = cms.InputTag("mix","MergedCaloTruth") + label_cp = cms.InputTag("mix","MergedCaloTruth"), + filter_sim_hits = cms.string("Ecal") ) hltPFClusterTesterECAL = cms.EDProducer("PFClusterTester", From f7927ccf4e1084f64511ae9a6eee2c0bab669a47 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 26 Feb 2026 18:17:32 +0100 Subject: [PATCH 35/64] Rename hits_energies with representing fractions. --- Validation/RecoParticleFlow/plugins/PFTester.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 84708cc69ab3b..e1368d35a9477 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -779,9 +779,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (auto const& hit_energy : recoClusters[recoPair.first.index()].recHitFractions()) { DetId id(hit_energy.recHitRef()->detId()); const GlobalPoint pos = caloGeom.getPosition(id); - edm::LogPrint("PFTester") << " DetId=" << hit_energy.recHitRef()->detId() << ", eta=" << pos.eta() - << ", phi=" << pos.phi() << ", en=" << hit_energy.recHitRef()->energy() - << ", fr=" << hit_energy.fraction(); + edm::LogPrint("PFTester") << " DetId=" << hit_fraction.recHitRef()->detId() << ", eta=" << pos.eta() + << ", phi=" << pos.phi() << ", en=" << hit_fraction.recHitRef()->energy() + << ", fr=" << hit_fraction.fraction(); } #endif From ac1c6005b4db817bca87be113b96411384923c9b Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 26 Feb 2026 18:17:52 +0100 Subject: [PATCH 36/64] Remove redundant assert. --- SimDataFormats/CaloAnalysis/interface/SimCluster.h | 1 - 1 file changed, 1 deletion(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index b38527c36e62d..5f1a9f3ca44a7 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -411,7 +411,6 @@ class SimCluster { void assertFinalized_() const { assert(hitsFinalized_ && "SimCluster: hits not finalized. Call finalizeHits() in the producer before persisting."); - assert(hits_.size() == fractions_.size()); } void buildDetRanges_() { From 132b7bf9825d9ab004bec82a3005e02fcc1e23c9 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 26 Feb 2026 18:33:47 +0100 Subject: [PATCH 37/64] Ensure hits and fractions are not empty. --- SimDataFormats/CaloAnalysis/interface/SimCluster.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index 5f1a9f3ca44a7..bf5ec9cf67803 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -288,7 +288,7 @@ class SimCluster { // -------------------------------------------------------------------------- void finalizeHits() { // Keep your original implicit invariant: - assert(hits_.size() == fractions_.size()); + assert(hits_.size() == fractions_.size() && !hits_.empty()); // Energies are optional but if present must align. if (!energies_.empty()) assert(energies_.size() == hits_.size()); From eff415559a2533acfe60cf57d963ba875063cd44 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Mon, 16 Mar 2026 00:17:12 +0100 Subject: [PATCH 38/64] Add views to PF cluster validation. --- .../LCToCPAssociatorByEnergyScoreImpl.cc | 22 +- .../LCToCPAssociatorByEnergyScoreImpl.h | 10 +- .../plugins/LCToCPAssociatorEDProducer.cc | 32 +- .../LCToSCAssociatorByEnergyScoreImpl.cc | 23 +- .../LCToSCAssociatorByEnergyScoreImpl.h | 8 +- .../plugins/LCToSCAssociatorEDProducer.cc | 27 +- ...rClusterToCaloParticleAssociatorBaseImpl.h | 10 +- .../LayerClusterToSimClusterAssociator.h | 10 +- ...yerClusterToSimClusterAssociatorBaseImpl.h | 8 +- ...ClusterToCaloParticleAssociatorBaseImpl.cc | 8 +- ...erClusterToSimClusterAssociatorBaseImpl.cc | 8 +- .../CaloAnalysis/interface/SimCluster.h | 206 ++++++---- .../CaloAnalysis/interface/SimClusterFwd.h | 11 + .../CaloAnalysis/src/SimClusterUtils.cc | 35 ++ .../RecoParticleFlow/plugins/PFTester.cc | 368 ++++++++++-------- .../python/hltPFValidation_cfi.py | 9 +- 16 files changed, 493 insertions(+), 302 deletions(-) create mode 100644 SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc index a357102fa9b4e..c114aa1096fc9 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc @@ -33,7 +33,9 @@ LCToCPAssociatorByEnergyScoreImplT::LCToCPAssociatorByEnergyScoreI template ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnections( - const edm::Handle& cCCH, const edm::Handle& cPCH, const std::vector& detIds) const { + const edm::Handle& cCCH, + const edm::Handle& cPCH, + const std::vector& detIds) const { // Get collections const auto& clusters = *cCCH.product(); const auto& caloParticles = *cPCH.product(); @@ -76,12 +78,12 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti for (const auto& it_sc : simClusterRefVector) { const SimCluster& simCluster = (*(it_sc)); - SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? simCluster.hits_and_fractions_view() : - (detIds.size() == 1 ? simCluster.hits_and_fractions_view(detIds[0]) : - simCluster.hits_and_fractions_view(*detIds.begin(), *(detIds.rbegin()))); + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simCluster.hits_and_fractions_view() + : simCluster.hits_and_fractions_view(*std::min_element(detIds.begin(), detIds.end()), + *std::max_element(detIds.begin(), detIds.end())); - for (size_t i = 0; i < hafView.hits.size(); ++i) { + for (size_t i = 0; i < hafView.size(); ++i) { const uint32_t hitid = hafView.hits[i]; const float fraction = hafView.fractions[i]; @@ -543,7 +545,9 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti template ticl::RecoToSimCollectionT LCToCPAssociatorByEnergyScoreImplT::associateRecoToSim( - const edm::Handle& cCCH, const edm::Handle& cPCH, const std::vector& detIds) const { + const edm::Handle& cCCH, + const edm::Handle& cPCH, + const std::vector& detIds) const { ticl::RecoToSimCollectionT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { @@ -569,7 +573,9 @@ ticl::RecoToSimCollectionT LCToCPAssociatorByEnergyScoreImplT ticl::SimToRecoCollectionT LCToCPAssociatorByEnergyScoreImplT::associateSimToReco( - const edm::Handle& cCCH, const edm::Handle& cPCH, const std::vector& detIds) const { + const edm::Handle& cCCH, + const edm::Handle& cPCH, + const std::vector& detIds) const { ticl::SimToRecoCollectionT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h index dd5420880a60c..ca8697bbab599 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.h @@ -77,11 +77,13 @@ class LCToCPAssociatorByEnergyScoreImplT : public ticl::LayerClusterToCaloPartic const std::unordered_map *, const multiCollectionT &hits); - ticl::RecoToSimCollectionT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const override; + ticl::RecoToSimCollectionT associateRecoToSim(const edm::Handle &cCH, + const edm::Handle &cPCH, + const std::vector &detIds) const override; - ticl::SimToRecoCollectionT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const override; + ticl::SimToRecoCollectionT associateSimToReco(const edm::Handle &cCH, + const edm::Handle &cPCH, + const std::vector &detIds) const override; private: const bool hardScatterOnly_; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc index 39e072e05342e..dbd53c177ddc2 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc @@ -23,6 +23,7 @@ #include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" #include "DataFormats/CaloRecHit/interface/CaloClusterFwd.h" #include "DataFormats/CaloRecHit/interface/CaloCluster.h" +#include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "DataFormats/ParticleFlowReco/interface/PFClusterFwd.h" #include "DataFormats/ParticleFlowReco/interface/PFCluster.h" #include "DataFormats/HGCRecHit/interface/HGCRecHitCollections.h" @@ -39,26 +40,17 @@ class LCToCPAssociatorEDProducerT : public edm::global::EDProducer<> { explicit LCToCPAssociatorEDProducerT(const edm::ParameterSet &); ~LCToCPAssociatorEDProducerT() override = default; - // static void fillDescriptions(edm::ConfigurationDescriptions &descriptions); - static void fillDescriptions(edm::ConfigurationDescriptions &descriptions); private: void produce(edm::StreamID, edm::Event &, const edm::EventSetup &) const override; edm::InputTag label_lc; - std::string filter_sim_hits_; + std::vector filter_sim_hits; edm::EDGetTokenT CPCollectionToken_; edm::EDGetTokenT LCCollectionToken_; edm::EDGetTokenT> associatorToken_; - - const std::unordered_map> DetIdMap = { - {"", std::vector{}}, - {"Ecal", std::vector{DetId::Ecal}}, - {"Hcal", std::vector{DetId::Hcal}}, - {"HGCal", std::vector{DetId::HGCalEE, DetId::HGCalHSc}} - }; }; template @@ -67,7 +59,9 @@ LCToCPAssociatorEDProducerT::LCToCPAssociatorEDProducerT(const edm::Par produces>(); label_lc = pset.getParameter("label_lc"); - filter_sim_hits_ = pset.getParameter("filter_sim_hits"); + filter_sim_hits = pset.getParameter>("filter_sim_hits"); + + simcluster_utils::check_detids(filter_sim_hits); CPCollectionToken_ = consumes(pset.getParameter("label_cp")); LCCollectionToken_ = consumes(label_lc); @@ -84,8 +78,6 @@ template void LCToCPAssociatorEDProducerT::produce(edm::StreamID, edm::Event &iEvent, const edm::EventSetup &iSetup) const { - using namespace edm; - edm::Handle> theAssociator; iEvent.getByToken(associatorToken_, theAssociator); @@ -94,14 +86,14 @@ void LCToCPAssociatorEDProducerT::produce(edm::StreamID, return; } - Handle CPCollection; + edm::Handle CPCollection; iEvent.getByToken(CPCollectionToken_, CPCollection); if (!CPCollection.isValid()) { edm::LogWarning("LCToCPAssociatorEDProducerT") << "CaloParticle collection is unavailable."; return; } - Handle LCCollection; + edm::Handle LCCollection; iEvent.getByToken(LCCollectionToken_, LCCollection); // Protection against missing cluster collection @@ -118,12 +110,16 @@ void LCToCPAssociatorEDProducerT::produce(edm::StreamID, return; } + std::vector detIds = simcluster_utils::join_detids(filter_sim_hits); + // associate LC and CP LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; - ticl::RecoToSimCollectionT recSimColl = theAssociator->associateRecoToSim(LCCollection, CPCollection, DetIdMap.at(filter_sim_hits_)); + ticl::RecoToSimCollectionT recSimColl = + theAssociator->associateRecoToSim(LCCollection, CPCollection, detIds); LogTrace("AssociatorValidator") << "Calling associateSimToReco method\n"; - ticl::SimToRecoCollectionT simRecColl = theAssociator->associateSimToReco(LCCollection, CPCollection, DetIdMap.at(filter_sim_hits_)); + ticl::SimToRecoCollectionT simRecColl = + theAssociator->associateSimToReco(LCCollection, CPCollection, detIds); auto rts = std::make_unique>(recSimColl); auto str = std::make_unique>(simRecColl); @@ -136,7 +132,7 @@ template void LCToCPAssociatorEDProducerT::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { edm::ParameterSetDescription desc; desc.add("label_cp", edm::InputTag("mix", "MergedCaloTruth")); - desc.ifValue(edm::ParameterDescription("filter_sim_hits", "", true), edm::allowedValues("", "Ecal", "Hcal", "HGCal")); + desc.add>("filter_sim_hits", {""}); desc.add("label_lc", edm::InputTag("hgcalMergeLayerClusters")); desc.add("associator", edm::InputTag("lcAssocByEnergyScoreProducer")); descriptions.addWithDefaultLabel(desc); diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index 952e7c136d2a7..e897408ec5268 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -5,6 +5,8 @@ #include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "DataFormats/CaloRecHit/interface/CaloCluster.h" +#include + template LCToSCAssociatorByEnergyScoreImplT::LCToSCAssociatorByEnergyScoreImplT( edm::EDProductGetter const& productGetter, @@ -25,7 +27,9 @@ LCToSCAssociatorByEnergyScoreImplT::LCToSCAssociatorByEnergyScoreI template ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnections( - const edm::Handle& cCCH, const edm::Handle& sCCH, const std::vector& detIds) const { + const edm::Handle& cCCH, + const edm::Handle& sCCH, + const std::vector& detIds) const { // Get collections const auto& clusters = *cCCH.product(); const auto& simClusters = *sCCH.product(); @@ -74,10 +78,10 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti edm::MultiSpan hitsMS(hits_); for (const auto& scId : sCIndices) { - SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? simClusters[scId].hits_and_fractions_view() : - (detIds.size() == 1 ? simClusters[scId].hits_and_fractions_view(detIds[0]) : - simClusters[scId].hits_and_fractions_view(*detIds.begin(), *(detIds.rbegin()))); + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simClusters[scId].hits_and_fractions_view() + : simClusters[scId].hits_and_fractions_view(*std::min_element(detIds.begin(), detIds.end()), + *std::max_element(detIds.begin(), detIds.end())); for (size_t i = 0; i < hafView.hits.size(); ++i) { const uint32_t hitid = hafView.hits[i]; @@ -178,6 +182,7 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti if (hit_find_in_SC != detIdToSimClusterId_Map.end()) { const auto itcheck = hitMap_->find(rh_detid); const HIT* hit = &hitsMS[itcheck->second]; + //Loops through all the simclusters that have the layer cluster rechit under study //Here is time to update the lcsInSimCluster and connect the SimCluster with all //the layer clusters that have the current rechit detid matched. @@ -556,7 +561,9 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti template ticl::RecoToSimCollectionWithSimClustersT LCToSCAssociatorByEnergyScoreImplT::associateRecoToSim( - const edm::Handle& cCCH, const edm::Handle& sCCH, const std::vector& detIds) const { + const edm::Handle& cCCH, + const edm::Handle& sCCH, + const std::vector& detIds) const { ticl::RecoToSimCollectionWithSimClustersT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { @@ -582,7 +589,9 @@ ticl::RecoToSimCollectionWithSimClustersT LCToSCAssociatorByEnergyScore template ticl::SimToRecoCollectionWithSimClustersT LCToSCAssociatorByEnergyScoreImplT::associateSimToReco( - const edm::Handle& cCCH, const edm::Handle& sCCH, const std::vector& detIds) const { + const edm::Handle& cCCH, + const edm::Handle& sCCH, + const std::vector& detIds) const { ticl::SimToRecoCollectionWithSimClustersT returnValue(productGetter_); if (!hitMap_ || hitMap_->empty()) { diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h index c29bb34b95f69..c7c348fc61fba 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.h @@ -75,10 +75,14 @@ class LCToSCAssociatorByEnergyScoreImplT : public ticl::LayerClusterToSimCluster const multiCollectionT &hits); ticl::RecoToSimCollectionWithSimClustersT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const override; + const edm::Handle &cCH, + const edm::Handle &sCCH, + const std::vector &detIds) const override; ticl::SimToRecoCollectionWithSimClustersT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const override; + const edm::Handle &cCH, + const edm::Handle &sCCH, + const std::vector &detIds) const override; private: const bool hardScatterOnly_; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index 8a01acf904ea9..b047fae70dd43 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -23,6 +23,8 @@ #include "FWCore/Utilities/interface/EDGetToken.h" #include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" +#include + // // class declaration // @@ -40,18 +42,12 @@ class LCToSCAssociatorEDProducerT : public edm::global::EDProducer<> { edm::InputTag label_lcl; edm::InputTag label_scl; - std::string filter_sim_hits_; + std::vector filter_sim_hits; edm::EDGetTokenT SCCollectionToken_; edm::EDGetTokenT LCCollectionToken_; edm::EDGetTokenT> associatorToken_; - const std::unordered_map> DetIdMap = { - {"", std::vector{}}, - {"Ecal", std::vector{DetId::Ecal}}, - {"Hcal", std::vector{DetId::Hcal}}, - {"HGCal", std::vector{DetId::HGCalEE, DetId::HGCalHSc}} - }; }; template @@ -61,7 +57,8 @@ LCToSCAssociatorEDProducerT::LCToSCAssociatorEDProducerT(const edm::Par label_lcl = pset.getParameter("label_lcl"); label_scl = pset.getParameter("label_scl"); - filter_sim_hits_ = pset.getParameter("filter_sim_hits"); + filter_sim_hits = pset.getParameter>("filter_sim_hits"); + simcluster_utils::check_detids(filter_sim_hits); LCCollectionToken_ = consumes(label_lcl); SCCollectionToken_ = consumes(label_scl); @@ -78,8 +75,6 @@ template void LCToSCAssociatorEDProducerT::produce(edm::StreamID, edm::Event &iEvent, const edm::EventSetup &iSetup) const { - using namespace edm; - edm::Handle> theAssociator; iEvent.getByToken(associatorToken_, theAssociator); @@ -88,7 +83,7 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, return; } - Handle SCCollection; + edm::Handle SCCollection; iEvent.getByToken(SCCollectionToken_, SCCollection); if (!SCCollection.isValid()) { @@ -98,7 +93,7 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, return; } - Handle LCCollection; + edm::Handle LCCollection; iEvent.getByToken(LCCollectionToken_, LCCollection); // Protections @@ -119,14 +114,16 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, return; } + std::vector detIds = simcluster_utils::join_detids(filter_sim_hits); + // associate LC and SC LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; ticl::RecoToSimCollectionWithSimClustersT recSimColl = - theAssociator->associateRecoToSim(LCCollection, SCCollection, DetIdMap.at(filter_sim_hits_)); + theAssociator->associateRecoToSim(LCCollection, SCCollection, detIds); LogTrace("AssociatorValidator") << "Calling associateSimToReco method\n"; ticl::SimToRecoCollectionWithSimClustersT simRecColl = - theAssociator->associateSimToReco(LCCollection, SCCollection, DetIdMap.at(filter_sim_hits_)); + theAssociator->associateSimToReco(LCCollection, SCCollection, detIds); auto rts = std::make_unique>(recSimColl); auto str = std::make_unique>(simRecColl); @@ -139,7 +136,7 @@ template void LCToSCAssociatorEDProducerT::fillDescriptions(edm::ConfigurationDescriptions &descriptions) { edm::ParameterSetDescription desc; desc.add("label_scl", edm::InputTag("mix", "MergedCaloTruth")); - desc.ifValue(edm::ParameterDescription("filter_sim_hits", "", true), edm::allowedValues("", "Ecal", "Hcal", "HGCal")); + desc.add>("filter_sim_hits", {""}); desc.add("label_lcl", edm::InputTag("hgcalMergeLayerClusters")); desc.add("associator", edm::InputTag("scAssocByEnergyScoreProducer")); descriptions.addWithDefaultLabel(desc); diff --git a/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h b/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h index 3af69a8997222..9f300e70eff35 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h +++ b/SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociatorBaseImpl.h @@ -35,12 +35,14 @@ namespace ticl { virtual ~LayerClusterToCaloParticleAssociatorBaseImplT(); /// Associate a LayerCluster to CaloParticles - virtual ticl::RecoToSimCollectionT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const; + virtual ticl::RecoToSimCollectionT associateRecoToSim(const edm::Handle &cCH, + const edm::Handle &cPCH, + const std::vector &detIds) const; /// Associate a CaloParticle to LayerClusters - virtual ticl::SimToRecoCollectionT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &cPCH, const std::vector &detIds) const; + virtual ticl::SimToRecoCollectionT associateSimToReco(const edm::Handle &cCH, + const edm::Handle &cPCH, + const std::vector &detIds) const; }; } // namespace ticl diff --git a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h index f7ae1a44053d4..f7a0795031d36 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h +++ b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h @@ -30,14 +30,16 @@ namespace ticl { delete; // stop default // ---------- const member functions --------------------- /// Associate a LayerCluster to SimClusters - RecoToSimCollectionWithSimClustersT associateRecoToSim( - const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { + RecoToSimCollectionWithSimClustersT associateRecoToSim(const edm::Handle &cCCH, + const edm::Handle &sCCH, + const std::vector &detIds) const { return m_impl->associateRecoToSim(cCCH, sCCH, detIds); }; /// Associate a SimCluster to LayerClusters - SimToRecoCollectionWithSimClustersT associateSimToReco( - const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { + SimToRecoCollectionWithSimClustersT associateSimToReco(const edm::Handle &cCCH, + const edm::Handle &sCCH, + const std::vector &detIds) const { return m_impl->associateSimToReco(cCCH, sCCH, detIds); } diff --git a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h index 229b9ff2548f0..b20d04bc19fb6 100644 --- a/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h +++ b/SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociatorBaseImpl.h @@ -36,11 +36,15 @@ namespace ticl { /// Associate a LayerCluster to SimClusters virtual RecoToSimCollectionWithSimClustersT associateRecoToSim( - const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const; + const edm::Handle &cCH, + const edm::Handle &sCCH, + const std::vector &detIds) const; /// Associate a SimCluster to LayerClusters virtual SimToRecoCollectionWithSimClustersT associateSimToReco( - const edm::Handle &cCH, const edm::Handle &sCCH, const std::vector &detIds) const; + const edm::Handle &cCH, + const edm::Handle &sCCH, + const std::vector &detIds) const; }; } // namespace ticl diff --git a/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc b/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc index b2b29c500e16a..dd48b30973019 100644 --- a/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc +++ b/SimDataFormats/Associations/src/LayerClusterToCaloParticleAssociatorBaseImpl.cc @@ -10,13 +10,17 @@ namespace ticl { template ticl::RecoToSimCollectionT LayerClusterToCaloParticleAssociatorBaseImplT::associateRecoToSim( - const edm::Handle &cCCH, const edm::Handle &cPCH, const std::vector &detIds) const { + const edm::Handle &cCCH, + const edm::Handle &cPCH, + const std::vector &detIds) const { return ticl::RecoToSimCollectionT(); } template ticl::SimToRecoCollectionT LayerClusterToCaloParticleAssociatorBaseImplT::associateSimToReco( - const edm::Handle &cCCH, const edm::Handle &cPCH, const std::vector &detIds) const { + const edm::Handle &cCCH, + const edm::Handle &cPCH, + const std::vector &detIds) const { return ticl::SimToRecoCollectionT(); } } // namespace ticl diff --git a/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc b/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc index 36137c392098d..b06c2b950f80b 100644 --- a/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc +++ b/SimDataFormats/Associations/src/LayerClusterToSimClusterAssociatorBaseImpl.cc @@ -10,13 +10,17 @@ namespace ticl { template RecoToSimCollectionWithSimClustersT LayerClusterToSimClusterAssociatorBaseImplT::associateRecoToSim( - const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { + const edm::Handle &cCCH, + const edm::Handle &sCCH, + const std::vector &detIds) const { return RecoToSimCollectionWithSimClustersT(); } template SimToRecoCollectionWithSimClustersT LayerClusterToSimClusterAssociatorBaseImplT::associateSimToReco( - const edm::Handle &cCCH, const edm::Handle &sCCH, const std::vector &detIds) const { + const edm::Handle &cCCH, + const edm::Handle &sCCH, + const std::vector &detIds) const { return SimToRecoCollectionWithSimClustersT(); } diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index bf5ec9cf67803..43500bdbb2db1 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -42,9 +42,10 @@ class SimCluster { typedef std::vector::const_iterator g4t_iterator; // Zero-copy hit+fraction view (iterates as pairs) - struct HitsAndFractionsView { + // Not used directly; use the named derived structs below. + struct HitsAndValuesViewBase { std::span hits; - std::span fractions; + std::span values; struct iterator { using iterator_category = std::random_access_iterator_tag; @@ -53,18 +54,38 @@ class SimCluster { using reference = value_type; // returned by value using pointer = void; - const uint32_t* h = nullptr; - const float* f = nullptr; - - reference operator*() const { return {*h, *f}; } - - iterator& operator++() { ++h; ++f; return *this; } - iterator operator++(int) { auto tmp = *this; ++(*this); return tmp; } - iterator& operator--() { --h; --f; return *this; } - iterator operator--(int) { auto tmp = *this; --(*this); return tmp; } - - iterator& operator+=(difference_type n) { h += n; f += n; return *this; } - iterator& operator-=(difference_type n) { return (*this) += (-n); } + const uint32_t *h = nullptr; + const float *v = nullptr; + + reference operator*() const { return {*h, *v}; } + + iterator &operator++() { + ++h; + ++v; + return *this; + } + iterator operator++(int) { + auto tmp = *this; + ++(*this); + return tmp; + } + iterator &operator--() { + --h; + --v; + return *this; + } + iterator operator--(int) { + auto tmp = *this; + --(*this); + return tmp; + } + + iterator &operator+=(difference_type n) { + h += n; + v += n; + return *this; + } + iterator &operator-=(difference_type n) { return (*this) += (-n); } friend iterator operator+(iterator it, difference_type n) { return it += n; } friend iterator operator-(iterator it, difference_type n) { return it -= n; } @@ -75,16 +96,28 @@ class SimCluster { friend bool operator<(iterator a, iterator b) { return a.h < b.h; } }; - iterator begin() const { return iterator{hits.data(), fractions.data()}; } + iterator begin() const { return iterator{hits.data(), values.data()}; } iterator end() const { return iterator{hits.data() + static_cast(hits.size()), - fractions.data() + static_cast(fractions.size())}; + values.data() + static_cast(values.size())}; + } + + iterator::value_type operator[](size_t n) const { + assert(n < values.size()); + return {hits[n], values[n]}; } size_t size() const { return hits.size(); } bool empty() const { return hits.empty(); } }; + struct HitsAndFractionsView : HitsAndValuesViewBase { + std::span &fractions = values; + }; + struct HitsAndEnergiesView : HitsAndValuesViewBase { + std::span &energies = values; + }; + SimCluster() = default; SimCluster(const SimTrack &simtrk); SimCluster(EncodedEventId eventID, uint32_t particleID); // for PU @@ -237,7 +270,7 @@ class SimCluster { // legacy returns a copy; now deterministic because finalizeHits() sorted it std::vector> result; result.reserve(hits_.size()); - for (size_t i = 0; i < hits_.size(); ++i) { + for (size_t i = 0; i < hits_.size(); ++i) { result.emplace_back(hits_[i], fractions_[i]); } return result; @@ -249,7 +282,8 @@ class SimCluster { std::vector> result; for (size_t i = 0; i < hits_.size(); ++i) { DetId detid(hits_[i]); - if (predicate(detid)) result.emplace_back(hits_[i], fractions_[i]); + if (predicate(detid)) + result.emplace_back(hits_[i], fractions_[i]); } return result; } @@ -259,7 +293,8 @@ class SimCluster { assert(hits_.size() == energies_.size()); std::vector> result; result.reserve(hits_.size()); - for (size_t i = 0; i < hits_.size(); ++i) result.emplace_back(hits_[i], energies_[i]); + for (size_t i = 0; i < hits_.size(); ++i) + result.emplace_back(hits_[i], energies_[i]); return result; } @@ -290,10 +325,12 @@ class SimCluster { // Keep your original implicit invariant: assert(hits_.size() == fractions_.size() && !hits_.empty()); // Energies are optional but if present must align. - if (!energies_.empty()) assert(energies_.size() == hits_.size()); + if (!energies_.empty()) + assert(energies_.size() == hits_.size()); // Already finalized? keep it cheap and idempotent. - if (hitsFinalized_) return; + if (hitsFinalized_) + return; // Sort by (det, subdet, rawid) std::vector order(hits_.size()); @@ -308,7 +345,8 @@ class SimCluster { applyPermutation_(hits_, order); applyPermutation_(fractions_, order); - if (!energies_.empty()) applyPermutation_(energies_, order); + if (!energies_.empty()) + applyPermutation_(energies_, order); buildDetRanges_(); @@ -316,69 +354,56 @@ class SimCluster { } // -------------------------------------------------------------------------- - // cost-free views (require finalizeHits has been called) + // cost-free views (requires a call to finalizeHits()) // -------------------------------------------------------------------------- HitsAndFractionsView hits_and_fractions_view() const { assertFinalized_(); - return HitsAndFractionsView{std::span(hits_.data(), hits_.size()), - std::span(fractions_.data(), fractions_.size())}; + return HitsAndFractionsView{{hits_, fractions_}}; } HitsAndFractionsView hits_and_fractions_view(DetId::Detector det) const { assertFinalized_(); - const auto idx = detIndex_(det); - auto [b, e] = detRanges_[idx]; - return HitsAndFractionsView{std::span(hits_.data() + b, e - b), - std::span(fractions_.data() + b, e - b)}; + auto [b, e] = detRanges_[detIndex_(det)]; + return HitsAndFractionsView{{{hits_.data() + b, e - b}, {fractions_.data() + b, e - b}}}; } - // det + subdet (still zero-copy; uses binary search within the det block) HitsAndFractionsView hits_and_fractions_view(DetId::Detector det, int subdetId) const { assertFinalized_(); - const auto idx = detIndex_(det); - auto [b, e] = detRanges_[idx]; - if (b == e) return HitsAndFractionsView{}; - - auto beginIt = hits_.begin() + static_cast(b); - auto endIt = hits_.begin() + static_cast(e); - - auto keyOf = [](uint32_t rawid) { - DetId id(rawid); - return std::pair(id.subdetId(), rawid); - }; - - const auto lowKey = std::pair(subdetId, 0u); - const auto highKey = std::pair(subdetId, std::numeric_limits::max()); - - auto lo = std::lower_bound(beginIt, endIt, lowKey, - [&](uint32_t rawid, const auto &k) { return keyOf(rawid) < k; }); - auto hi = std::upper_bound(beginIt, endIt, highKey, - [&](const auto &k, uint32_t rawid) { return k < keyOf(rawid); }); - - const size_t bb = static_cast(std::distance(hits_.begin(), lo)); - const size_t ee = static_cast(std::distance(hits_.begin(), hi)); - - return HitsAndFractionsView{std::span(hits_.data() + bb, ee - bb), - std::span(fractions_.data() + bb, ee - bb)}; + auto [bb, ee] = subdetRange_(det, subdetId); + return HitsAndFractionsView{{{hits_.data() + bb, ee - bb}, {fractions_.data() + bb, ee - bb}}}; } - // min, max detids HitsAndFractionsView hits_and_fractions_view(DetId::Detector detIdMin, DetId::Detector detIdMax) const { assertFinalized_(); + auto [begin, end] = detMinMaxRange_(detIdMin, detIdMax); + return HitsAndFractionsView{{{hits_.data() + begin, end - begin}, {fractions_.data() + begin, end - begin}}}; + } - // Find the first and last indices using the same ranges used - // for the per-detector lookup. - const auto idxMin = detIndex_(detIdMin); - const auto idxMax = detIndex_(detIdMax); + HitsAndEnergiesView hits_and_energies_view() const { + assertFinalized_(); + assertEnergies_(); + return HitsAndEnergiesView{{hits_, energies_}}; + } - const auto [bMin, eMin] = detRanges_[idxMin]; - const auto [bMax, eMax] = detRanges_[idxMax]; + HitsAndEnergiesView hits_and_energies_view(DetId::Detector det) const { + assertFinalized_(); + assertEnergies_(); + auto [b, e] = detRanges_[detIndex_(det)]; + return HitsAndEnergiesView{{{hits_.data() + b, e - b}, {energies_.data() + b, e - b}}}; + } - const uint32_t begin = bMin; // inclusive - const uint32_t end = eMax; // exclusive + HitsAndEnergiesView hits_and_energies_view(DetId::Detector det, int subdetId) const { + assertFinalized_(); + assertEnergies_(); + auto [bb, ee] = subdetRange_(det, subdetId); + return HitsAndEnergiesView{{{hits_.data() + bb, ee - bb}, {energies_.data() + bb, ee - bb}}}; + } - return HitsAndFractionsView{std::span(hits_.data() + begin, end - begin), - std::span(fractions_.data() + begin, end - begin)}; + HitsAndEnergiesView hits_and_energies_view(DetId::Detector detIdMin, DetId::Detector detIdMax) const { + assertFinalized_(); + assertEnergies_(); + auto [begin, end] = detMinMaxRange_(detIdMin, detIdMax); + return HitsAndEnergiesView{{{hits_.data() + begin, end - begin}, {energies_.data() + begin, end - begin}}}; } protected: @@ -398,7 +423,7 @@ class SimCluster { reco::GenParticleRefVector genParticles_; private: - static constexpr size_t kMaxDetectors_ = 32; // Probably 16 could be enough + static constexpr size_t kMaxDetectors_ = 32; // Probably 16 could be enough bool hitsFinalized_{false}; std::array, kMaxDetectors_> detRanges_{}; // [begin,end) per detector @@ -413,6 +438,46 @@ class SimCluster { assert(hitsFinalized_ && "SimCluster: hits not finalized. Call finalizeHits() in the producer before persisting."); } + void assertEnergies_() const { + assert(!energies_.empty() && + "SimCluster: energies is empty; populate it with addHitEnergy() before calling this view."); + } + + // Returns the [begin, end) index range within hits_ for a det+subdet pair. + std::pair subdetRange_(DetId::Detector det, int subdetId) const { + auto [b, e] = detRanges_[detIndex_(det)]; + if (b == e) + return {b, e}; + + auto beginIt = hits_.begin() + static_cast(b); + auto endIt = hits_.begin() + static_cast(e); + + auto keyOf = [](uint32_t rawid) { + DetId id(rawid); + return std::pair(id.subdetId(), rawid); + }; + + const auto lowKey = std::pair(subdetId, 0u); + const auto highKey = std::pair(subdetId, std::numeric_limits::max()); + + auto lo = std::lower_bound(beginIt, endIt, lowKey, [&](uint32_t rawid, const auto &k) { return keyOf(rawid) < k; }); + auto hi = + std::upper_bound(beginIt, endIt, highKey, [&](const auto &k, uint32_t rawid) { return k < keyOf(rawid); }); + + return {static_cast(std::distance(hits_.begin(), lo)), + static_cast(std::distance(hits_.begin(), hi))}; + } + + // Returns the [begin, end) index range spanning all detectors in [detIdMin, detIdMax]. + std::pair detMinMaxRange_(DetId::Detector detIdMin, DetId::Detector detIdMax) const { + const auto detIndexMin = detIndex_(detIdMin); + const auto detIndexMax = detIndex_(detIdMax); + assert(detIndexMin <= detIndexMax); + const auto [bMin, eMin] = detRanges_[detIndexMin]; + const auto [bMax, eMax] = detRanges_[detIndexMax]; + return {bMin, eMax}; + } + void buildDetRanges_() { detRanges_.fill({0u, 0u}); size_t i = 0; @@ -420,7 +485,9 @@ class SimCluster { DetId id(hits_[i]); const auto idx = detIndex_(static_cast(id.det())); const size_t begin = i; - do { ++i; } while (i < hits_.size() && DetId(hits_[i]).det() == id.det()); + do { + ++i; + } while (i < hits_.size() && DetId(hits_[i]).det() == id.det()); detRanges_[idx] = {begin, i}; } } @@ -429,7 +496,8 @@ class SimCluster { static void applyPermutation_(std::vector &v, const std::vector &order) { std::vector tmp; tmp.reserve(v.size()); - for (size_t idx : order) tmp.push_back(v[idx]); + for (size_t idx : order) + tmp.push_back(v[idx]); v.swap(tmp); } }; diff --git a/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h b/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h index 4f11d4bcc86ed..10192ee84a40d 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h +++ b/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h @@ -3,7 +3,9 @@ #include "DataFormats/Common/interface/Ref.h" #include "DataFormats/Common/interface/RefProd.h" #include "DataFormats/Common/interface/RefVector.h" +#include "DataFormats/DetId/interface/DetId.h" #include +#include class SimCluster; std::ostream &operator<<(std::ostream &s, SimCluster const &tp); @@ -14,4 +16,13 @@ typedef edm::RefVector SimClusterRefVector; typedef edm::RefProd SimClusterRefProd; typedef edm::RefVector SimClusterContainer; +std::ostream &operator<<(std::ostream &s, SimCluster const &tp); + +namespace simcluster_utils { + extern const std::unordered_map> DetIdMap; + + std::vector join_detids(const std::vector &dets_v); + void check_detids(const std::vector &dets_v); +} // namespace simcluster_utils + #endif diff --git a/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc b/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc new file mode 100644 index 0000000000000..004dee7406baf --- /dev/null +++ b/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc @@ -0,0 +1,35 @@ +#include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" + +namespace simcluster_utils { + const std::unordered_map> DetIdMap = { + {"", std::vector{}}, + {"Ecal", std::vector{DetId::Ecal}}, + {"Hcal", std::vector{DetId::Hcal}}, + {"HGCal", std::vector{DetId::HGCalEE, DetId::HGCalHSi, DetId::HGCalHSc}}}; + + // Build the merged set of detectors from all requested filter strings + std::vector join_detids(const std::vector& dets_v) { + std::vector detIds; + for (const auto& det : dets_v) { + const auto& dets = simcluster_utils::DetIdMap.at(det); + detIds.insert(detIds.end(), dets.begin(), dets.end()); + } + return detIds; + } + + void check_detids(const std::vector& dets_v) { + std::vector allowed(simcluster_utils::DetIdMap.size()); + int i = 0; + for (auto it = simcluster_utils::DetIdMap.begin(); it != simcluster_utils::DetIdMap.end(); ++it) { + allowed[i] = it->first; // store key + ++i; + } + + for (const auto& det : dets_v) { + if (std::find(allowed.begin(), allowed.end(), det) == allowed.end()) { + throw cms::Exception("Configuration") << "dets_v: unknown value '" << det << "'. " + << "Allowed values are: Ecal, Hcal, HGCal, or empty string."; + } + } + } +} // namespace simcluster_utils diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index e1368d35a9477..cb507964b72ec 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -11,8 +11,8 @@ #include "DataFormats/ParticleFlowReco/interface/PFRecTrack.h" #include "DataFormats/ParticleFlowReco/interface/PFCluster.h" #include "DataFormats/CaloRecHit/interface/CaloCluster.h" -#include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "SimDataFormats/CaloAnalysis/interface/CaloParticle.h" +#include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h" #include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" #include "SimDataFormats/Associations/interface/LayerClusterToCaloParticleAssociator.h" @@ -33,8 +33,8 @@ class PFTesterT : public DQMEDAnalyzer { void bookHistograms(DQMStore::IBooker&, edm::Run const&, edm::EventSetup const&) override; void analyze(const edm::Event&, const edm::EventSetup&) override; std::string doubleToString(double x) const; - double simClusterEnergy(const SimCluster& sc) const; - double recoClusterEnergyWeightedBySimFraction(const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const; + double simClusterEnergy(const SimCluster::HitsAndEnergiesView& enView) const; + double recoClusterEnergyWeightedBySimFraction(const SimCluster::HitsAndFractionsView& fracView, const reco::PFRecHitCollection& pfrechits) const; edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; @@ -44,6 +44,9 @@ class PFTesterT : public DQMEDAnalyzer { edm::EDGetTokenT SimClusterToken_; edm::EDGetTokenT> RecoToSimAssociatorToken_; edm::EDGetTokenT> SimToRecoAssociatorToken_; + + std::vector filter_sim_hits_; + edm::EDGetTokenT> RecoToCpAssociatorToken_; edm::EDGetTokenT> CpToRecoAssociatorToken_; @@ -208,6 +211,7 @@ PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) iConfig.getParameter("ClusterSimClusterAssociator"))), SimToRecoAssociatorToken_(consumes>( iConfig.getParameter("ClusterSimClusterAssociator"))), + filter_sim_hits_(iConfig.getParameter>("filter_sim_hits")), RecoToCpAssociatorToken_(consumes>( iConfig.getParameter("ClusterCaloParticleAssociator"))), CpToRecoAssociatorToken_(consumes>( @@ -229,6 +233,8 @@ PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) h2d_responseE_.resize(nAssocScoreThresholds_); h_nSimMatchedToOneReco_.resize(nAssocScoreThresholds_); h_nRecoMatchedToOneSim_.resize(nAssocScoreThresholds_); + + simcluster_utils::check_detids(filter_sim_hits_); } template @@ -413,7 +419,6 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, hMinY, hMaxY); } - for (unsigned ithr = 0; ithr < nAssocScoreThresholds_; ++ithr) { std::string threshStr = "Score" + doubleToString(assocScoreThresholds_[ithr]); ibook.setCurrentFolder(pfValidFolder + "/" + threshStr); @@ -544,6 +549,13 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, template void PFTesterT::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { + std::vector detIds = simcluster_utils::join_detids(filter_sim_hits_); + DetId::Detector minDet, maxDet; + if (!detIds.empty()) { + minDet = *std::min_element(detIds.begin(), detIds.end()); + maxDet = *std::max_element(detIds.begin(), detIds.end()); + } + // -------------------------------------------------------------------- // ---------------- PF Clusters and associators ----------------------- // -------------------------------------------------------------------- @@ -618,7 +630,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- // ----- Calo Particles plots ----------------------------------------- // -------------------------------------------------------------------- - std::unordered_map simClusterToCPEnergyMap; for (unsigned int cpId = 0; cpId < caloParticles.size(); ++cpId) { // Fill map: for each simCluster, the energy of the caloParticle computed as the sum of all simClusters arising from it @@ -627,12 +638,18 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e double recoEnergySumWeightedBySimFrac = 0; for (const auto& scRef : caloParticles[cpId].simClusters()) { auto const& sc = *(scRef); + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? sc.hits_and_fractions_view() : sc.hits_and_fractions_view(minDet, maxDet); + SimCluster::HitsAndEnergiesView haeView = + detIds.empty() ? sc.hits_and_energies_view() : sc.hits_and_energies_view(minDet, maxDet); + // Compute energy of caloParticle as sum of simClusters energies (from SimTrack energy) energySumSimClusters += sc.energy(); // Compute energy of caloParticle as sum of all hits from all simClusters - energySumSimHits += simClusterEnergy(sc); + + energySumSimHits += simClusterEnergy(haeView); // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters - recoEnergySumWeightedBySimFrac += recoClusterEnergyWeightedBySimFraction(sc, pfRechit); + recoEnergySumWeightedBySimFrac += recoClusterEnergyWeightedBySimFraction(hafView, pfRechit); } for (const auto& scRef : caloParticles[cpId].simClusters()) { simClusterToCPEnergyMap[scRef.key()] = energySumSimHits; @@ -689,56 +706,62 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e uint nSimClusters = 0; uint nSimClustersPrimary = 0; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = simClusterEnergy(simClusters[simId]); - h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simClusters[simId].energy()); + const auto& simCluster = simClusters[simId]; + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); + SimCluster::HitsAndEnergiesView haeView = + detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); - double recoEnergySumWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); + double energySumSimHits = simClusterEnergy(haeView); + h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simCluster.energy()); + + double recoEnergySumWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(hafView, pfRechit); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; if (SimClusterToCPEnergyFraction < enFracCut_) continue; // apply cut on pt of the sim track - if (simClusters[simId].pt() < ptCut_) + if (simCluster.pt() < ptCut_) continue; - + // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel - auto const scTrack = simClusters[simId].g4Tracks()[0]; + auto const scTrack = simCluster.g4Tracks()[0]; const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); auto const simTrackEtaAtBoundary = pos.Eta(); if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; - + ++nSimClusters; - if (simClusters[simId].g4Tracks()[0].isPrimary()) + if (simCluster.g4Tracks()[0].isPrimary()) ++nSimClustersPrimary; // efficiency and split denominator h_simClusters_["En"]->Fill(energySumSimHits); h_simClusters_["EnFrac"]->Fill(SimClusterToCPEnergyFraction); - h_simClusters_["EnSimTrack"]->Fill(simClusters[simId].energy()); - h_simClusters_["Pt"]->Fill(simClusters[simId].pt()); - h_simClusters_["PtLow"]->Fill(simClusters[simId].pt()); + h_simClusters_["EnSimTrack"]->Fill(simCluster.energy()); + h_simClusters_["Pt"]->Fill(simCluster.pt()); + h_simClusters_["PtLow"]->Fill(simCluster.pt()); h_simClusters_["Eta"]->Fill(simTrackEtaAtBoundary); - h_simClusters_["Phi"]->Fill(simClusters[simId].phi()); - h_simClusters_["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + h_simClusters_["Phi"]->Fill(simCluster.phi()); + h_simClusters_["Mult"]->Fill(simCluster.numberOfRecHits()); h2d_simClusters_["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); - h2d_simClusters_["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClusters_["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); - h2d_simClusters_["En_Mult"]->Fill(energySumSimHits, simClusters[simId].numberOfRecHits()); + h2d_simClusters_["En_Phi"]->Fill(energySumSimHits, simCluster.phi()); + h2d_simClusters_["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simCluster.phi()); + h2d_simClusters_["En_Mult"]->Fill(energySumSimHits, simCluster.numberOfRecHits()); h2d_simClusters_["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); - h2d_simClusters_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].phi()); - h2d_simClusters_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simClusters[simId].numberOfRecHits()); - h2d_simClusters_["EnSimTrack_Eta"]->Fill(simClusters[simId].energy(), simTrackEtaAtBoundary); - h2d_simClusters_["EnSimTrack_Phi"]->Fill(simClusters[simId].energy(), simClusters[simId].phi()); - h2d_simClusters_["EnSimTrack_Mult"]->Fill(simClusters[simId].energy(), simClusters[simId].numberOfRecHits()); - h2d_simClusters_["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); - h2d_simClusters_["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); - h2d_simClusters_["Pt_Mult"]->Fill(simClusters[simId].pt(), simClusters[simId].numberOfRecHits()); - h2d_simClusters_["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), simTrackEtaAtBoundary); - h2d_simClusters_["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), simClusters[simId].phi()); + h2d_simClusters_["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simCluster.phi()); + h2d_simClusters_["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simCluster.numberOfRecHits()); + h2d_simClusters_["EnSimTrack_Eta"]->Fill(simCluster.energy(), simTrackEtaAtBoundary); + h2d_simClusters_["EnSimTrack_Phi"]->Fill(simCluster.energy(), simCluster.phi()); + h2d_simClusters_["EnSimTrack_Mult"]->Fill(simCluster.energy(), simCluster.numberOfRecHits()); + h2d_simClusters_["Pt_Eta"]->Fill(simCluster.pt(), simTrackEtaAtBoundary); + h2d_simClusters_["Pt_Phi"]->Fill(simCluster.pt(), simCluster.phi()); + h2d_simClusters_["Pt_Mult"]->Fill(simCluster.pt(), simCluster.numberOfRecHits()); + h2d_simClusters_["Mult_Eta"]->Fill(simCluster.numberOfRecHits(), simTrackEtaAtBoundary); + h2d_simClusters_["Mult_Phi"]->Fill(simCluster.numberOfRecHits(), simCluster.phi()); const edm::Ref simClusterRef(SimCluster, simId); const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); @@ -748,41 +771,51 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e if (simToRecoMatched.empty()) continue; +#ifdef debug + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + + auto ev = simCluster.g4Tracks()[0].eventId().event(); + auto bx = simCluster.g4Tracks()[0].eventId().bunchCrossing(); + edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx + << ", en=" << energySumSimHits + << ", nhits=" << hafView.size() + << ", hits="; + + for (size_t it = 0; itdetId()); - const GlobalPoint pos = caloGeom.getPosition(id); - edm::LogPrint("PFTester") << " DetId=" << hit_fraction.recHitRef()->detId() << ", eta=" << pos.eta() - << ", phi=" << pos.phi() << ", en=" << hit_fraction.recHitRef()->energy() - << ", fr=" << hit_fraction.fraction(); - } + const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); + + if constexpr (std::is_same::value) { + edm::LogPrint("PFTester") << " Matched to RecoCluster[" << recoPair.first.index() + << "], en=" << recoClusters[recoPair.first.index()].energy() + << ", with shared energy: " << recoPair.second.first + << ", shared energy fraction: " << recoPair.second.first / energyFracSumSimHits + << ", score: " << recoPair.second.second + << ", score threshold: " << thresh + << ", nhits: " << recoClusters[recoPair.first.index()].recHitFractions().size() + << ", hits="; + + for (auto const& hit_fraction : recoClusters[recoPair.first.index()].recHitFractions()) { + DetId id(hit_fraction.recHitRef()->detId()); + const GlobalPoint pos = caloGeom.getPosition(id); + edm::LogPrint("PFTester") << " DetId=" << hit_fraction.recHitRef()->detId() << ", eta=" << pos.eta() + << ", phi=" << pos.phi() << ", en=" << hit_fraction.recHitRef()->energy() + << ", fr=" << hit_fraction.fraction(); + } + } #endif auto score = recoPair.second.second; @@ -794,12 +827,12 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h_simToRecoShEnF_Score_->Fill(shared_energy_frac, score); h_simToRecoShEnF_En_->Fill(shared_energy_frac, energySumSimHits); h_simToRecoShEnF_EnFrac_->Fill(shared_energy_frac, SimClusterToCPEnergyFraction); - h_simToRecoShEnF_EnSimTrack_->Fill(shared_energy_frac, simClusters[simId].energy()); - h_simToRecoShEnF_Mult_->Fill(shared_energy_frac, simClusters[simId].numberOfRecHits()); + h_simToRecoShEnF_EnSimTrack_->Fill(shared_energy_frac, simCluster.energy()); + h_simToRecoShEnF_Mult_->Fill(shared_energy_frac, simCluster.numberOfRecHits()); h_simToRecoScore_En_->Fill(score, energySumSimHits); h_simToRecoScore_EnFrac_->Fill(score, SimClusterToCPEnergyFraction); - h_simToRecoScore_EnSimTrack_->Fill(score, simClusters[simId].energy()); - h_simToRecoScore_Mult_->Fill(score, simClusters[simId].numberOfRecHits()); + h_simToRecoScore_EnSimTrack_->Fill(score, simCluster.energy()); + h_simToRecoScore_Mult_->Fill(score, simCluster.numberOfRecHits()); if (doMatchByScore_) { // cut on score @@ -813,54 +846,52 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e } } } - + // efficiency numerator if (nRecoMatchedToOneSim > 0) { h_simClustersMatchedRecoClusters_[ithr]["En"]->Fill(energySumSimHits); h_simClustersMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); - h_simClustersMatchedRecoClusters_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy()); - h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMatchedRecoClusters_[ithr]["EnSimTrack"]->Fill(simCluster.energy()); + h_simClustersMatchedRecoClusters_[ithr]["Pt"]->Fill(simCluster.pt()); + h_simClustersMatchedRecoClusters_[ithr]["PtLow"]->Fill(simCluster.pt()); h_simClustersMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); - h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); - h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + h_simClustersMatchedRecoClusters_[ithr]["Phi"]->Fill(simCluster.phi()); + h_simClustersMatchedRecoClusters_[ithr]["Mult"]->Fill(simCluster.numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(energySumSimHits, simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(energySumSimHits, simCluster.phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simCluster.phi()); h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(energySumSimHits, - simClusters[simId].numberOfRecHits()); + simCluster.numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, - simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simCluster.phi()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simClusters[simId].energy(), + simCluster.numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simCluster.energy(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Phi"]->Fill(simClusters[simId].energy(), - simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Mult"]->Fill(simClusters[simId].energy(), - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simClusters[simId].pt(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simClusters[simId].pt(), simClusters[simId].phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simClusters[simId].pt(), - simClusters[simId].numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simClusters[simId].numberOfRecHits(), + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Phi"]->Fill(simCluster.energy(), + simCluster.phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Mult"]->Fill(simCluster.energy(), + simCluster.numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simCluster.pt(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simCluster.pt(), simCluster.phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simCluster.pt(), + simCluster.numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simCluster.numberOfRecHits(), simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simClusters[simId].numberOfRecHits(), - simClusters[simId].phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simCluster.numberOfRecHits(), simCluster.phi()); // split numerator if (nRecoMatchedToOneSim > 1) { h_simClustersMultiMatchedRecoClusters_[ithr]["En"]->Fill(energySumSimHits); h_simClustersMultiMatchedRecoClusters_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction); - h_simClustersMultiMatchedRecoClusters_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy()); - h_simClustersMultiMatchedRecoClusters_[ithr]["Pt"]->Fill(simClusters[simId].pt()); - h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simClusters[simId].pt()); + h_simClustersMultiMatchedRecoClusters_[ithr]["EnSimTrack"]->Fill(simCluster.energy()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Pt"]->Fill(simCluster.pt()); + h_simClustersMultiMatchedRecoClusters_[ithr]["PtLow"]->Fill(simCluster.pt()); h_simClustersMultiMatchedRecoClusters_[ithr]["Eta"]->Fill(simTrackEtaAtBoundary); - h_simClustersMultiMatchedRecoClusters_[ithr]["Phi"]->Fill(simClusters[simId].phi()); - h_simClustersMultiMatchedRecoClusters_[ithr]["Mult"]->Fill(simClusters[simId].numberOfRecHits()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Phi"]->Fill(simCluster.phi()); + h_simClustersMultiMatchedRecoClusters_[ithr]["Mult"]->Fill(simCluster.numberOfRecHits()); } } @@ -876,26 +907,28 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { + const auto& recoCluster = recoClusters[recoId]; + // fake and merge denominator - h_recoClusters_["En"]->Fill(recoClusters[recoId].energy()); - h_recoClusters_["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClusters_["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClusters_["Mult"]->Fill(recoClusters[recoId].size()); + h_recoClusters_["En"]->Fill(recoCluster.energy()); + h_recoClusters_["Eta"]->Fill(recoCluster.eta()); + h_recoClusters_["Phi"]->Fill(recoCluster.phi()); + h_recoClusters_["Mult"]->Fill(recoCluster.size()); if constexpr (std::is_same::value) { - h_recoClusters_["Time"]->Fill(recoClusters[recoId].time()); + h_recoClusters_["Time"]->Fill(recoCluster.time()); } - h2d_recoClusters_["En_Eta"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].eta()); - h2d_recoClusters_["En_Phi"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].phi()); - h2d_recoClusters_["Eta_Phi"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].phi()); - h2d_recoClusters_["En_Mult"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].size()); - h2d_recoClusters_["Mult_Eta"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].eta()); - h2d_recoClusters_["Mult_Phi"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].phi()); + h2d_recoClusters_["En_Eta"]->Fill(recoCluster.energy(), recoCluster.eta()); + h2d_recoClusters_["En_Phi"]->Fill(recoCluster.energy(), recoCluster.phi()); + h2d_recoClusters_["Eta_Phi"]->Fill(recoCluster.eta(), recoCluster.phi()); + h2d_recoClusters_["En_Mult"]->Fill(recoCluster.energy(), recoCluster.size()); + h2d_recoClusters_["Mult_Eta"]->Fill(recoCluster.size(), recoCluster.eta()); + h2d_recoClusters_["Mult_Phi"]->Fill(recoCluster.size(), recoCluster.phi()); if constexpr (std::is_same::value) { - h2d_recoClusters_["En_Time"]->Fill(recoClusters[recoId].energy(), recoClusters[recoId].time()); - h2d_recoClusters_["Eta_Time"]->Fill(recoClusters[recoId].eta(), recoClusters[recoId].time()); - h2d_recoClusters_["Phi_Time"]->Fill(recoClusters[recoId].phi(), recoClusters[recoId].time()); - h2d_recoClusters_["Mult_Time"]->Fill(recoClusters[recoId].size(), recoClusters[recoId].time()); + h2d_recoClusters_["En_Time"]->Fill(recoCluster.energy(), recoCluster.time()); + h2d_recoClusters_["Eta_Time"]->Fill(recoCluster.eta(), recoCluster.time()); + h2d_recoClusters_["Phi_Time"]->Fill(recoCluster.phi(), recoCluster.time()); + h2d_recoClusters_["Mult_Time"]->Fill(recoCluster.size(), recoCluster.time()); } // hits belonging to the clusters @@ -934,25 +967,29 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (const auto& simPair : recoToSimMatched) { const auto simPairIdx = simPair.first.index(); + const auto& simCluster = simClusters[simPairIdx]; + + SimCluster::HitsAndEnergiesView haeView = + detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); #ifdef debug edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId << " : matched simCluster id = " << simPairIdx << " score = " << simPair.second; #endif - double energySumSimHits = simClusterEnergy(simClusters[simPairIdx]); + double energySumSimHits = simClusterEnergy(haeView); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simPairIdx]; if (SimClusterToCPEnergyFraction < enFracCut_) continue; // apply cut on pt of the sim track - if (simClusters[simPairIdx].pt() < ptCut_) + if (simCluster.pt() < ptCut_) continue; // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel - auto const scTrack = simClusters[simPairIdx].g4Tracks()[0]; + auto const scTrack = simCluster.g4Tracks()[0]; const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); if (abs(pos.Eta()) > etaCut_) // simTrack does not cross the barrel continue; @@ -967,43 +1004,43 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // fake numerator if (nSimMatchedToOneReco > 0) { - h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); - - h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoClusters[recoId].eta(), - recoClusters[recoId].phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].size()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoClusters[recoId].size(), - recoClusters[recoId].eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoClusters[recoId].size(), - recoClusters[recoId].phi()); + h_recoClustersMatchedSimClusters_[ithr]["En"]->Fill(recoCluster.energy()); + h_recoClustersMatchedSimClusters_[ithr]["Eta"]->Fill(recoCluster.eta()); + h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoCluster.phi()); + h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoCluster.size()); + + h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoCluster.energy(), + recoCluster.eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoCluster.energy(), + recoCluster.phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoCluster.eta(), + recoCluster.phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoCluster.energy(), + recoCluster.size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoCluster.size(), + recoCluster.eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoCluster.size(), + recoCluster.phi()); if constexpr (std::is_same::value) { - h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoClusters[recoId].energy(), - recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoClusters[recoId].eta(), - recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoClusters[recoId].phi(), - recoClusters[recoId].time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoClusters[recoId].size(), - recoClusters[recoId].time()); + h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoCluster.energy(), + recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoCluster.eta(), + recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoCluster.phi(), + recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoCluster.size(), + recoCluster.time()); } // merge numerator if (nSimMatchedToOneReco > 1) { - h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoClusters[recoId].energy()); - h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoClusters[recoId].eta()); - h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoClusters[recoId].phi()); - h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoClusters[recoId].size()); + h_recoClustersMultiMatchedSimClusters_[ithr]["En"]->Fill(recoCluster.energy()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Eta"]->Fill(recoCluster.eta()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Phi"]->Fill(recoCluster.phi()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Mult"]->Fill(recoCluster.size()); if constexpr (std::is_same::value) { - h_recoClustersMultiMatchedSimClusters_[ithr]["Time"]->Fill(recoClusters[recoId].time()); + h_recoClustersMultiMatchedSimClusters_[ithr]["Time"]->Fill(recoCluster.time()); } } } @@ -1016,34 +1053,40 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // ----- Cluster response computation --------------------------------- // -------------------------------------------------------------------- for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - double energySumSimHits = simClusterEnergy(simClusters[simId]); + const auto& simCluster = simClusters[simId]; + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); + SimCluster::HitsAndEnergiesView haeView = + detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); + + double energySumSimHits = simClusterEnergy(haeView); - double recoEnergyWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(simClusters[simId], pfRechit); + double recoEnergyWeightedBySimFrac = recoClusterEnergyWeightedBySimFraction(hafView, pfRechit); // apply cut on energy fraction (sim cluster energy wrt all sim clusters from same calo particle) double SimClusterToCPEnergyFraction = energySumSimHits / simClusterToCPEnergyMap[simId]; if (SimClusterToCPEnergyFraction < enFracCut_) continue; // apply cut on pt of the sim track - if (simClusters[simId].pt() < ptCut_) + if (simCluster.pt() < ptCut_) continue; // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel - auto const scTrack = simClusters[simId].g4Tracks()[0]; + auto const scTrack = simCluster.g4Tracks()[0]; const math::XYZTLorentzVectorF& pos = scTrack.getPositionAtBoundary(); auto const simTrackEtaAtBoundary = pos.Eta(); if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; - const edm::Ref simClusterRef(SimCluster, simId); - const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); - if (simToRecoIt == simToRecoAssoc.end()) - continue; - const auto& simToRecoMatched = simToRecoIt->val; - if (simToRecoMatched.empty()) - continue; - + const edm::Ref simClusterRef(SimCluster, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + // they should already be sorted by score std::vector simToRecoMatchedSorted(simToRecoMatched.begin(), simToRecoMatched.end()); std::sort(simToRecoMatchedSorted.begin(), simToRecoMatchedSorted.end(), [](const auto& a, const auto& b) { @@ -1056,6 +1099,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // fill only the best matched (lowest score) reco cluster, regardless split or merge for (const auto& recoPair : simToRecoMatchedSorted) { auto recoId = recoPair.first.index(); + const auto& recoCluster = recoClusters[recoId]; bool passMatch = false; if (doMatchByScore_) { @@ -1069,7 +1113,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e } if (passMatch) { - float resp = recoClusters[recoId].energy() / energySumSimHits; + float resp = recoCluster.energy() / energySumSimHits; h2d_responseE_[ithr]["En"]->Fill(energySumSimHits, resp); h2d_responseE_[ithr]["EnFrac"]->Fill(SimClusterToCPEnergyFraction, resp); h2d_responseE_[ithr]["EnSimTrack"]->Fill(simClusters[simId].energy(), resp); @@ -1217,9 +1261,9 @@ std::string PFTesterT::doubleToString(double x) const { // compute the total energy of a simulated cluster template -double PFTesterT::simClusterEnergy(const SimCluster& sc) const { +double PFTesterT::simClusterEnergy(const SimCluster::HitsAndEnergiesView& enView) const { double energySumSimHits = 0; - for (auto hit_energy : sc.hits_and_energies()) { + for (auto hit_energy : enView) { energySumSimHits += hit_energy.second; } return energySumSimHits; @@ -1229,9 +1273,9 @@ double PFTesterT::simClusterEnergy(const SimCluster& sc) // the total energy of a reconstructed cluster, weighted by the sim cluster energy fraction template double PFTesterT::recoClusterEnergyWeightedBySimFraction( - const SimCluster& sc, const reco::PFRecHitCollection& pfrechits) const { + const SimCluster::HitsAndFractionsView& fracView, const reco::PFRecHitCollection& pfrechits) const { double recoEnergySumWeightedBySimFrac = 0; - for (auto hit_fraction : sc.hits_and_fractions()) { + for (auto hit_fraction : fracView) { DetId id(hit_fraction.first); auto rechitIt = std::find_if(pfrechits.begin(), pfrechits.end(), [id](const reco::PFRecHit& rh) { return rh.detId() == id; }); diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 2e3d1c6138750..c06351990411c 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -1,5 +1,7 @@ import FWCore.ParameterSet.Config as cms +_filter_sim_hits = cms.vstring("Ecal",) + hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), hitMapTag = cms.InputTag("hltRecHitMapProducer:pfRecHitMap"), @@ -10,7 +12,7 @@ associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), label_lcl = cms.InputTag("hltParticleFlowClusterECALUnseeded"), label_scl = cms.InputTag("mix","MergedCaloTruth"), - filter_sim_hits = cms.string("Ecal") + filter_sim_hits = _filter_sim_hits ) hltPFCpAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToCPAssociatorByEnergyScoreProducer", @@ -23,7 +25,7 @@ associator = cms.InputTag("hltPFCpAssocByEnergyScoreProducer"), label_lc = cms.InputTag("hltParticleFlowClusterECALUnseeded"), label_cp = cms.InputTag("mix","MergedCaloTruth"), - filter_sim_hits = cms.string("Ecal") + filter_sim_hits = _filter_sim_hits ) hltPFClusterTesterECAL = cms.EDProducer("PFClusterTester", @@ -34,12 +36,13 @@ CaloParticle = cms.InputTag("mix","MergedCaloTruth"), ClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), ClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), + filter_sim_hits = _filter_sim_hits, outFolder = cms.string('HLT/ParticleFlow'), assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.), - etaCut = cms.double(3.0) + etaCut = cms.double(3.0), ) from Configuration.Eras.Modifier_phase2_common_cff import phase2_common phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp"), etaCut = cms.double(1.48)) From 3841b5bc9adb662d9a93edbd52e8a9efc098d0b9 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 19 Mar 2026 17:16:59 +0100 Subject: [PATCH 39/64] Optimize detector minmax. --- .../plugins/LCToCPAssociatorByEnergyScoreImpl.cc | 13 ++++++++----- .../plugins/LCToSCAssociatorByEnergyScoreImpl.cc | 11 +++++++---- .../plugins/LCToSCAssociatorEDProducer.cc | 2 +- Validation/RecoParticleFlow/plugins/PFTester.cc | 8 +++++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc index c114aa1096fc9..276057fdc4fb3 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc @@ -78,11 +78,14 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti for (const auto& it_sc : simClusterRefVector) { const SimCluster& simCluster = (*(it_sc)); - SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? simCluster.hits_and_fractions_view() - : simCluster.hits_and_fractions_view(*std::min_element(detIds.begin(), detIds.end()), - *std::max_element(detIds.begin(), detIds.end())); - + SimCluster::HitsAndFractionsView hafView = + detIds.empty() + ? simCluster.hits_and_fractions_view() + : ([&detIds, &simCluster]() { + auto [minIt, maxIt] = std::minmax_element(detIds.begin(), detIds.end()); + return simCluster.hits_and_fractions_view(*minIt, *maxIt); + })(); + for (size_t i = 0; i < hafView.size(); ++i) { const uint32_t hitid = hafView.hits[i]; const float fraction = hafView.fractions[i]; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index e897408ec5268..f26ce2267cb41 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -78,10 +78,13 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti edm::MultiSpan hitsMS(hits_); for (const auto& scId : sCIndices) { - SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? simClusters[scId].hits_and_fractions_view() - : simClusters[scId].hits_and_fractions_view(*std::min_element(detIds.begin(), detIds.end()), - *std::max_element(detIds.begin(), detIds.end())); + SimCluster::HitsAndFractionsView hafView = + detIds.empty() + ? simClusters[scId].hits_and_fractions_view() + : ([&]() { + auto [minIt, maxIt] = std::minmax_element(detIds.begin(), detIds.end()); + return simClusters[scId].hits_and_fractions_view(*minIt, *maxIt); + })(); for (size_t i = 0; i < hafView.hits.size(); ++i) { const uint32_t hitid = hafView.hits[i]; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index b047fae70dd43..9b4ac4e9fe119 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -115,7 +115,7 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, } std::vector detIds = simcluster_utils::join_detids(filter_sim_hits); - + // associate LC and SC LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; ticl::RecoToSimCollectionWithSimClustersT recSimColl = diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index cb507964b72ec..f5850011080ed 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -550,10 +550,12 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, template void PFTesterT::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { std::vector detIds = simcluster_utils::join_detids(filter_sim_hits_); - DetId::Detector minDet, maxDet; + DetId::Detector minDet = DetId::Detector(0); //dummy detector id value + DetId::Detector maxDet = DetId::Detector(0); //dummy detector id value if (!detIds.empty()) { - minDet = *std::min_element(detIds.begin(), detIds.end()); - maxDet = *std::max_element(detIds.begin(), detIds.end()); + const auto minmaxDet = std::minmax_element(detIds.begin(), detIds.end()); + minDet = *minmaxDet.first; + maxDet = *minmaxDet.second; } // -------------------------------------------------------------------- From 86b915a5cbe502cb1af082e4fef5b429a9d71f15 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 19 Mar 2026 20:10:13 +0100 Subject: [PATCH 40/64] Code formats and checks. --- .../LCToCPAssociatorByEnergyScoreImpl.cc | 14 +- .../LCToSCAssociatorByEnergyScoreImpl.cc | 11 +- .../plugins/LCToSCAssociatorEDProducer.cc | 3 +- .../CaloAnalysis/interface/SimCluster.h | 2 +- .../RecoParticleFlow/plugins/PFTester.cc | 150 ++++++++---------- 5 files changed, 79 insertions(+), 101 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc index 276057fdc4fb3..4228e143a0153 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc @@ -78,14 +78,12 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti for (const auto& it_sc : simClusterRefVector) { const SimCluster& simCluster = (*(it_sc)); - SimCluster::HitsAndFractionsView hafView = - detIds.empty() - ? simCluster.hits_and_fractions_view() - : ([&detIds, &simCluster]() { - auto [minIt, maxIt] = std::minmax_element(detIds.begin(), detIds.end()); - return simCluster.hits_and_fractions_view(*minIt, *maxIt); - })(); - + SimCluster::HitsAndFractionsView hafView = + detIds.empty() ? simCluster.hits_and_fractions_view() : ([&detIds, &simCluster]() { + auto [minIt, maxIt] = std::minmax_element(detIds.begin(), detIds.end()); + return simCluster.hits_and_fractions_view(*minIt, *maxIt); + })(); + for (size_t i = 0; i < hafView.size(); ++i) { const uint32_t hitid = hafView.hits[i]; const float fraction = hafView.fractions[i]; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index f26ce2267cb41..55a748c6c3051 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -78,13 +78,10 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti edm::MultiSpan hitsMS(hits_); for (const auto& scId : sCIndices) { - SimCluster::HitsAndFractionsView hafView = - detIds.empty() - ? simClusters[scId].hits_and_fractions_view() - : ([&]() { - auto [minIt, maxIt] = std::minmax_element(detIds.begin(), detIds.end()); - return simClusters[scId].hits_and_fractions_view(*minIt, *maxIt); - })(); + SimCluster::HitsAndFractionsView hafView = detIds.empty() ? simClusters[scId].hits_and_fractions_view() : ([&]() { + auto [minIt, maxIt] = std::minmax_element(detIds.begin(), detIds.end()); + return simClusters[scId].hits_and_fractions_view(*minIt, *maxIt); + })(); for (size_t i = 0; i < hafView.hits.size(); ++i) { const uint32_t hitid = hafView.hits[i]; diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index 9b4ac4e9fe119..cb659f322d224 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -47,7 +47,6 @@ class LCToSCAssociatorEDProducerT : public edm::global::EDProducer<> { edm::EDGetTokenT SCCollectionToken_; edm::EDGetTokenT LCCollectionToken_; edm::EDGetTokenT> associatorToken_; - }; template @@ -115,7 +114,7 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, } std::vector detIds = simcluster_utils::join_detids(filter_sim_hits); - + // associate LC and SC LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; ticl::RecoToSimCollectionWithSimClustersT recSimColl = diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index 43500bdbb2db1..b305c9f4008e2 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -270,7 +270,7 @@ class SimCluster { // legacy returns a copy; now deterministic because finalizeHits() sorted it std::vector> result; result.reserve(hits_.size()); - for (size_t i = 0; i < hits_.size(); ++i) { + for (size_t i = 0; i < hits_.size(); ++i) { result.emplace_back(hits_[i], fractions_[i]); } return result; diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index f5850011080ed..199d31ae9f46f 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -34,7 +34,8 @@ class PFTesterT : public DQMEDAnalyzer { void analyze(const edm::Event&, const edm::EventSetup&) override; std::string doubleToString(double x) const; double simClusterEnergy(const SimCluster::HitsAndEnergiesView& enView) const; - double recoClusterEnergyWeightedBySimFraction(const SimCluster::HitsAndFractionsView& fracView, const reco::PFRecHitCollection& pfrechits) const; + double recoClusterEnergyWeightedBySimFraction(const SimCluster::HitsAndFractionsView& fracView, + const reco::PFRecHitCollection& pfrechits) const; edm::ESGetToken geometry_token_; edm::EDGetTokenT PFCandToken_; @@ -550,10 +551,10 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, template void PFTesterT::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { std::vector detIds = simcluster_utils::join_detids(filter_sim_hits_); - DetId::Detector minDet = DetId::Detector(0); //dummy detector id value - DetId::Detector maxDet = DetId::Detector(0); //dummy detector id value + DetId::Detector minDet = DetId::Detector(0); //dummy detector id value + DetId::Detector maxDet = DetId::Detector(0); //dummy detector id value if (!detIds.empty()) { - const auto minmaxDet = std::minmax_element(detIds.begin(), detIds.end()); + const auto minmaxDet = std::minmax_element(detIds.begin(), detIds.end()); minDet = *minmaxDet.first; maxDet = *minmaxDet.second; } @@ -709,7 +710,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e uint nSimClustersPrimary = 0; for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { const auto& simCluster = simClusters[simId]; - SimCluster::HitsAndFractionsView hafView = + SimCluster::HitsAndFractionsView hafView = detIds.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); SimCluster::HitsAndEnergiesView haeView = detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); @@ -726,7 +727,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // apply cut on pt of the sim track if (simCluster.pt() < ptCut_) continue; - + // filter all sim clusters produced by a sim track which crossed the // tracker/calorimeter boundary outside the barrel auto const scTrack = simCluster.g4Tracks()[0]; @@ -734,7 +735,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e auto const simTrackEtaAtBoundary = pos.Eta(); if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; - + ++nSimClusters; if (simCluster.g4Tracks()[0].isPrimary()) ++nSimClustersPrimary; @@ -774,50 +775,46 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e continue; #ifdef debug - const CaloGeometry& caloGeom = iSetup.getData(geometry_token_); - - auto ev = simCluster.g4Tracks()[0].eventId().event(); - auto bx = simCluster.g4Tracks()[0].eventId().bunchCrossing(); - edm::LogPrint("PFTester") << " SimCluster[" << simId << "], ev=" << ev << ", bx=" << bx - << ", en=" << energySumSimHits - << ", nhits=" << hafView.size() - << ", hits="; - - for (size_t it = 0; itFill(energySumSimHits); @@ -863,23 +860,19 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h2d_simClustersMatchedRecoClusters_[ithr]["En_Eta"]->Fill(energySumSimHits, simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["En_Phi"]->Fill(energySumSimHits, simCluster.phi()); h2d_simClustersMatchedRecoClusters_[ithr]["Eta_Phi"]->Fill(simTrackEtaAtBoundary, simCluster.phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(energySumSimHits, - simCluster.numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["En_Mult"]->Fill(energySumSimHits, simCluster.numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Eta"]->Fill(SimClusterToCPEnergyFraction, simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Phi"]->Fill(SimClusterToCPEnergyFraction, simCluster.phi()); h2d_simClustersMatchedRecoClusters_[ithr]["EnFrac_Mult"]->Fill(SimClusterToCPEnergyFraction, simCluster.numberOfRecHits()); - h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simCluster.energy(), - simTrackEtaAtBoundary); - h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Phi"]->Fill(simCluster.energy(), - simCluster.phi()); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Eta"]->Fill(simCluster.energy(), simTrackEtaAtBoundary); + h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Phi"]->Fill(simCluster.energy(), simCluster.phi()); h2d_simClustersMatchedRecoClusters_[ithr]["EnSimTrack_Mult"]->Fill(simCluster.energy(), simCluster.numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Eta"]->Fill(simCluster.pt(), simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Phi"]->Fill(simCluster.pt(), simCluster.phi()); - h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simCluster.pt(), - simCluster.numberOfRecHits()); + h2d_simClustersMatchedRecoClusters_[ithr]["Pt_Mult"]->Fill(simCluster.pt(), simCluster.numberOfRecHits()); h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Eta"]->Fill(simCluster.numberOfRecHits(), simTrackEtaAtBoundary); h2d_simClustersMatchedRecoClusters_[ithr]["Mult_Phi"]->Fill(simCluster.numberOfRecHits(), simCluster.phi()); @@ -909,7 +902,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // -------------------------------------------------------------------- h_nPFClusters_->Fill(recoClusters.size()); for (unsigned int recoId = 0; recoId < recoClusters.size(); ++recoId) { - const auto& recoCluster = recoClusters[recoId]; + const auto& recoCluster = recoClusters[recoId]; // fake and merge denominator h_recoClusters_["En"]->Fill(recoCluster.energy()); @@ -1011,28 +1004,18 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h_recoClustersMatchedSimClusters_[ithr]["Phi"]->Fill(recoCluster.phi()); h_recoClustersMatchedSimClusters_[ithr]["Mult"]->Fill(recoCluster.size()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoCluster.energy(), - recoCluster.eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoCluster.energy(), - recoCluster.phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoCluster.eta(), - recoCluster.phi()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoCluster.energy(), - recoCluster.size()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoCluster.size(), - recoCluster.eta()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoCluster.size(), - recoCluster.phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Eta"]->Fill(recoCluster.energy(), recoCluster.eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Phi"]->Fill(recoCluster.energy(), recoCluster.phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Phi"]->Fill(recoCluster.eta(), recoCluster.phi()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Mult"]->Fill(recoCluster.energy(), recoCluster.size()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Eta"]->Fill(recoCluster.size(), recoCluster.eta()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Phi"]->Fill(recoCluster.size(), recoCluster.phi()); if constexpr (std::is_same::value) { h_recoClustersMatchedSimClusters_[ithr]["Time"]->Fill(recoCluster.time()); - h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoCluster.energy(), - recoCluster.time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoCluster.eta(), - recoCluster.time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoCluster.phi(), - recoCluster.time()); - h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoCluster.size(), - recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["En_Time"]->Fill(recoCluster.energy(), recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Eta_Time"]->Fill(recoCluster.eta(), recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Phi_Time"]->Fill(recoCluster.phi(), recoCluster.time()); + h2d_recoClustersMatchedSimClusters_[ithr]["Mult_Time"]->Fill(recoCluster.size(), recoCluster.time()); } // merge numerator @@ -1055,7 +1038,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // ----- Cluster response computation --------------------------------- // -------------------------------------------------------------------- for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { - const auto& simCluster = simClusters[simId]; + const auto& simCluster = simClusters[simId]; SimCluster::HitsAndFractionsView hafView = detIds.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); SimCluster::HitsAndEnergiesView haeView = @@ -1081,14 +1064,14 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e if (abs(simTrackEtaAtBoundary) > etaCut_) // simTrack does not cross the barrel continue; - const edm::Ref simClusterRef(SimCluster, simId); - const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); - if (simToRecoIt == simToRecoAssoc.end()) - continue; - const auto& simToRecoMatched = simToRecoIt->val; - if (simToRecoMatched.empty()) - continue; - + const edm::Ref simClusterRef(SimCluster, simId); + const auto& simToRecoIt = simToRecoAssoc.find(simClusterRef); + if (simToRecoIt == simToRecoAssoc.end()) + continue; + const auto& simToRecoMatched = simToRecoIt->val; + if (simToRecoMatched.empty()) + continue; + // they should already be sorted by score std::vector simToRecoMatchedSorted(simToRecoMatched.begin(), simToRecoMatched.end()); std::sort(simToRecoMatchedSorted.begin(), simToRecoMatchedSorted.end(), [](const auto& a, const auto& b) { @@ -1304,6 +1287,7 @@ void PFTesterT::fillDescriptions(edm::ConfigurationDescri desc.add("ClusterCaloParticleAssociator", edm::InputTag("hltPFClusterCaloParticleAssociationProducerECAL")); desc.add>("assocScoreThresholds", {0.1}); + desc.add>("filter_sim_hits", {""}); desc.add("doMatchByScore", true); desc.add("enFracCut", 0.01); desc.add("ptCut", 0.1); From c420bda2202069b615ee59abff73be758c06c351 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 20 Mar 2026 00:45:28 +0100 Subject: [PATCH 41/64] Store numPFClusters as a histogram. --- Validation/RecoParticleFlow/plugins/PFTester.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 199d31ae9f46f..c8d0980893e88 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -66,6 +66,7 @@ class PFTesterT : public DQMEDAnalyzer { MonitorElement* h_NumECALElements_; MonitorElement* h_NumHCALElements_; MonitorElement* h_NumHGCALElements_; + MonitorElement* h_NumPFClusters_; MonitorElement* h_TrackCharge_; MonitorElement* h_TrackNumPoints_; @@ -529,6 +530,7 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, h_NumECALElements_ = ibook.book1D("NumECALElements", "NumECALElements", 5, 0, 5); h_NumHCALElements_ = ibook.book1D("NumHCALElements", "NumHCALElements", 5, 0, 5); h_NumHGCALElements_ = ibook.book1D("NumHGCALElements", "NumHGCALElements", 5, 0, 5); + h_NumPFClusters_ = ibook.book1D("NumPFClusters", "NumPFClusters", 150, 0, 150); ibook.setCurrentFolder(outFolder_ + "/" + matching + "/PFTracks"); h_TrackCharge_ = ibook.book1D("TrackCharge", "TrackCharge", 5, -2, 2); @@ -1225,6 +1227,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e h_NumECALElements_->Fill(numECALElements); h_NumHCALElements_->Fill(numHCALElements); h_NumHGCALElements_->Fill(numHGCALElements); + h_NumPFClusters_->Fill(numPFClusters); } } From f831f81a3734c74f41b5a52dc77bb31e83861aef Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 20 Mar 2026 17:30:04 +0100 Subject: [PATCH 42/64] Update MtdSimLayerCluster and MtdSimTrackster class versions. --- SimDataFormats/CaloAnalysis/src/classes_def.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/src/classes_def.xml b/SimDataFormats/CaloAnalysis/src/classes_def.xml index 5dd6992a11cbf..e828ff3b679d2 100644 --- a/SimDataFormats/CaloAnalysis/src/classes_def.xml +++ b/SimDataFormats/CaloAnalysis/src/classes_def.xml @@ -53,9 +53,8 @@ - - - + + @@ -64,8 +63,8 @@ - - + + From 66beb7ce8c6c3d5ff1aa337d2b13d612be52f339 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Mon, 23 Mar 2026 13:31:16 +0100 Subject: [PATCH 43/64] Add hit energy in calo truth accumulators. --- SimDataFormats/CaloAnalysis/interface/SimCluster.h | 2 +- SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc | 4 ++-- .../CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc | 2 ++ Validation/RecoParticleFlow/plugins/PFTester.cc | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index b305c9f4008e2..9c0ff22bc0d01 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -319,7 +319,7 @@ class SimCluster { } // -------------------------------------------------------------------------- - // New: producer-side "finalization" (to be called before putting in the event) + // Producer-side "finalization" (to be called before putting in the event) // -------------------------------------------------------------------------- void finalizeHits() { // Keep your original implicit invariant: diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index d9bf1204c0af9..5d04ab1b34b5c 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -358,6 +358,7 @@ namespace { void doEndCluster(SimClassNameT &cluster) { for (auto const &hit_and_energy : acc_energy) { cluster.addRecHitAndFraction(hit_and_energy.first, hit_and_energy.second); + cluster.addHitEnergy(hit_and_energy.second); } acc_energy.clear(); } @@ -737,9 +738,8 @@ void CaloTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup cons std::copy(m_detIdToTotalSimEnergy.begin(), m_detIdToTotalSimEnergy.end(), std::back_inserter(*totalEnergies)); std::sort(totalEnergies->begin(), totalEnergies->end()); event.put(std::move(totalEnergies), "MergedCaloTruth"); - // make sure persisted SimClusters have sorted hits and built ranges for (auto &sc : *(output_.pSimClusters)) { - sc.finalizeHits(); + sc.finalizeHits(); // make sure persisted SimClusters have sorted hits and built ranges } } else { applyToSimClusterConfig( diff --git a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc index 2184600387969..2b24a41aa60a5 100644 --- a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc +++ b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc @@ -133,6 +133,7 @@ void PreMixingCaloParticleWorker::put(edm::Event &iEvent, for (auto &sc : *newClusters_) { auto hitsAndEnergies = sc.hits_and_fractions(); sc.clearHitsAndFractions(); + sc.clearHitsEnergy(); for (auto &hAndE : hitsAndEnergies) { const float totalenergy = totalEnergy_[hAndE.first]; float fraction = 0.; @@ -142,6 +143,7 @@ void PreMixingCaloParticleWorker::put(edm::Event &iEvent, edm::LogWarning("PreMixingParticleWorker") << "TotalSimEnergy for hit " << hAndE.first << " is 0! The fraction for this hit cannot be computed."; sc.addRecHitAndFraction(hAndE.first, fraction); + sc.addHitEnergy(hAndE.second); } } diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index c8d0980893e88..79fadafa12b14 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -651,7 +651,6 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e // Compute energy of caloParticle as sum of simClusters energies (from SimTrack energy) energySumSimClusters += sc.energy(); // Compute energy of caloParticle as sum of all hits from all simClusters - energySumSimHits += simClusterEnergy(haeView); // Compute energy of caloParticle as sum of all rechits energy multiplied by sim fraction from all simClusters recoEnergySumWeightedBySimFrac += recoClusterEnergyWeightedBySimFraction(hafView, pfRechit); From e74b1adcdd5137837a240a2cb5724936dd3a1b9e Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 24 Mar 2026 17:39:28 +0100 Subject: [PATCH 44/64] Fix filter_sim_hits type. --- Validation/Configuration/python/hltHGCalSimValid_cff.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Validation/Configuration/python/hltHGCalSimValid_cff.py b/Validation/Configuration/python/hltHGCalSimValid_cff.py index 165524bf21106..df93c7522f243 100644 --- a/Validation/Configuration/python/hltHGCalSimValid_cff.py +++ b/Validation/Configuration/python/hltHGCalSimValid_cff.py @@ -82,13 +82,13 @@ hltBarrelLayerClusterCaloParticleAssociationProducer = _barrelLayerClusterCaloParticleAssociation.clone( associator = cms.InputTag("hltBarrelLcAssocByEnergyScoreProducer"), label_lc = cms.InputTag("hltBarrelLayerClustersEB"), - filter_sim_hits = cms.string("Ecal") + filter_sim_hits = cms.vstring("Ecal",) ) hltBarrelLayerClusterSimClusterAssociationProducer = _barrelLayerClusterSimClusterAssociation.clone( associator = cms.InputTag("hltBarrelScAssocByEnergyScoreProducer"), label_lcl = cms.InputTag("hltBarrelLayerClustersEB"), - filter_sim_hits = cms.string("Ecal") + filter_sim_hits = cms.vstring("Ecal",) ) hltHgcalAndBarrelLayerClustersAssociatorsTask = cms.Task( From ecf57dcb04a57a1f525b1347dd013dcceb9dc3c9 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Wed, 25 Mar 2026 22:24:27 +0100 Subject: [PATCH 45/64] Add hit finalization to premixing step. --- SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc index 2b24a41aa60a5..fb463931821f3 100644 --- a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc +++ b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc @@ -145,6 +145,7 @@ void PreMixingCaloParticleWorker::put(edm::Event &iEvent, sc.addRecHitAndFraction(hAndE.first, fraction); sc.addHitEnergy(hAndE.second); } + sc.finalizeHits(); } // clear memory From 59b73b6324cc52989429487da10afe91238cf211 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 26 Mar 2026 14:59:08 +0100 Subject: [PATCH 46/64] Remove threshold value to reduce memory consumption. --- Validation/HGCalValidation/python/HLTHGCalValidator_cff.py | 4 ++-- Validation/RecoParticleFlow/python/hltPFValidation_cfi.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py index d6bff18ac851c..ab23663c5a7e8 100644 --- a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py +++ b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py @@ -56,7 +56,7 @@ ClusterSimClusterAssociator = cms.InputTag("hltBarrelLayerClusterSimClusterAssociationProducer"), ClusterCaloParticleAssociator = cms.InputTag("hltBarrelLayerClusterCaloParticleAssociationProducer"), outFolder = cms.string('HLT/TiclBarrel'), - assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + assocScoreThresholds = cms.vdouble(1.1, 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.), @@ -89,4 +89,4 @@ ) from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel -ticl_barrel.toReplaceWith(hltHgcalValSeq, hltHgcalAndBarrelValSeq) \ No newline at end of file +ticl_barrel.toReplaceWith(hltHgcalValSeq, hltHgcalAndBarrelValSeq) diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index c06351990411c..45c11df474c1d 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -38,7 +38,7 @@ ClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), filter_sim_hits = _filter_sim_hits, outFolder = cms.string('HLT/ParticleFlow'), - assocScoreThresholds = cms.vdouble(1.1, 0.9, 0.5, 0.1), + assocScoreThresholds = cms.vdouble(1.1, 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.), From 465c2b127b25ada944247eccccd3753194c6ac35 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 27 Mar 2026 16:06:33 +0100 Subject: [PATCH 47/64] Move detIds to constructors. --- .../plugins/LCToCPAssociatorEDProducer.cc | 19 ++++++------- .../plugins/LCToSCAssociatorEDProducer.cc | 28 +++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc index dbd53c177ddc2..90bb993bf9598 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc @@ -45,8 +45,8 @@ class LCToCPAssociatorEDProducerT : public edm::global::EDProducer<> { private: void produce(edm::StreamID, edm::Event &, const edm::EventSetup &) const override; - edm::InputTag label_lc; - std::vector filter_sim_hits; + edm::InputTag label_lc_; + std::vector detIds_; edm::EDGetTokenT CPCollectionToken_; edm::EDGetTokenT LCCollectionToken_; @@ -58,13 +58,14 @@ LCToCPAssociatorEDProducerT::LCToCPAssociatorEDProducerT(const edm::Par produces>(); produces>(); - label_lc = pset.getParameter("label_lc"); - filter_sim_hits = pset.getParameter>("filter_sim_hits"); + label_lc_ = pset.getParameter("label_lc"); + std::vector filter_sim_hits = pset.getParameter>("filter_sim_hits"); simcluster_utils::check_detids(filter_sim_hits); + detIds_ = simcluster_utils::join_detids(filter_sim_hits); CPCollectionToken_ = consumes(pset.getParameter("label_cp")); - LCCollectionToken_ = consumes(label_lc); + LCCollectionToken_ = consumes(label_lc_); associatorToken_ = consumes>(pset.getParameter("associator")); } @@ -99,7 +100,7 @@ void LCToCPAssociatorEDProducerT::produce(edm::StreamID, // Protection against missing cluster collection if (!LCCollection.isValid()) { edm::LogWarning("LCToCPAssociatorEDProducerT") - << "CaloCluster collection with label " << label_lc << " is unavailable. Producing empty associations."; + << "CaloCluster collection with label " << label_lc_ << " is unavailable. Producing empty associations."; // Return empty collections auto emptyRecSimColl = std::make_unique>(); @@ -110,16 +111,14 @@ void LCToCPAssociatorEDProducerT::produce(edm::StreamID, return; } - std::vector detIds = simcluster_utils::join_detids(filter_sim_hits); - // associate LC and CP LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; ticl::RecoToSimCollectionT recSimColl = - theAssociator->associateRecoToSim(LCCollection, CPCollection, detIds); + theAssociator->associateRecoToSim(LCCollection, CPCollection, detIds_); LogTrace("AssociatorValidator") << "Calling associateSimToReco method\n"; ticl::SimToRecoCollectionT simRecColl = - theAssociator->associateSimToReco(LCCollection, CPCollection, detIds); + theAssociator->associateSimToReco(LCCollection, CPCollection, detIds_); auto rts = std::make_unique>(recSimColl); auto str = std::make_unique>(simRecColl); diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index cb659f322d224..5656b10980420 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -40,9 +40,9 @@ class LCToSCAssociatorEDProducerT : public edm::global::EDProducer<> { private: void produce(edm::StreamID, edm::Event &, const edm::EventSetup &) const override; - edm::InputTag label_lcl; - edm::InputTag label_scl; - std::vector filter_sim_hits; + edm::InputTag label_lcl_; + edm::InputTag label_scl_; + std::vector detIds_; edm::EDGetTokenT SCCollectionToken_; edm::EDGetTokenT LCCollectionToken_; @@ -54,13 +54,15 @@ LCToSCAssociatorEDProducerT::LCToSCAssociatorEDProducerT(const edm::Par produces>(); produces>(); - label_lcl = pset.getParameter("label_lcl"); - label_scl = pset.getParameter("label_scl"); - filter_sim_hits = pset.getParameter>("filter_sim_hits"); + label_lcl_ = pset.getParameter("label_lcl"); + label_scl_ = pset.getParameter("label_scl"); + + std::vector filter_sim_hits = pset.getParameter>("filter_sim_hits"); simcluster_utils::check_detids(filter_sim_hits); + detIds_ = simcluster_utils::join_detids(filter_sim_hits); - LCCollectionToken_ = consumes(label_lcl); - SCCollectionToken_ = consumes(label_scl); + LCCollectionToken_ = consumes(label_lcl_); + SCCollectionToken_ = consumes(label_scl_); associatorToken_ = consumes>(pset.getParameter("associator")); } @@ -98,11 +100,11 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, // Protections if (!SCCollection.isValid()) { edm::LogWarning("LCToSCAssociatorEDProducerT") - << "CaloCluster collection with label " << label_scl << " is unavailable. Producing empty associations."; + << "CaloCluster collection with label " << label_scl_ << " is unavailable. Producing empty associations."; } if (!LCCollection.isValid()) { edm::LogWarning("LCToSCAssociatorEDProducer") - << "CaloCluster collection with label " << label_lcl << " is unavailable. Producing empty associations."; + << "CaloCluster collection with label " << label_lcl_ << " is unavailable. Producing empty associations."; // Return empty collections auto emptyRecSimColl = std::make_unique>(); @@ -113,16 +115,14 @@ void LCToSCAssociatorEDProducerT::produce(edm::StreamID, return; } - std::vector detIds = simcluster_utils::join_detids(filter_sim_hits); - // associate LC and SC LogTrace("AssociatorValidator") << "Calling associateRecoToSim method\n"; ticl::RecoToSimCollectionWithSimClustersT recSimColl = - theAssociator->associateRecoToSim(LCCollection, SCCollection, detIds); + theAssociator->associateRecoToSim(LCCollection, SCCollection, detIds_); LogTrace("AssociatorValidator") << "Calling associateSimToReco method\n"; ticl::SimToRecoCollectionWithSimClustersT simRecColl = - theAssociator->associateSimToReco(LCCollection, SCCollection, detIds); + theAssociator->associateSimToReco(LCCollection, SCCollection, detIds_); auto rts = std::make_unique>(recSimColl); auto str = std::make_unique>(simRecColl); From 322aafa4199eebeb867088f09c0d19b6315732af Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 27 Mar 2026 16:38:45 +0100 Subject: [PATCH 48/64] Use unordered_map has O(1) search. --- .../plugins/LCToCPAssociatorEDProducer.cc | 4 +-- .../plugins/LCToSCAssociatorEDProducer.cc | 3 +- .../CaloAnalysis/interface/SimClusterFwd.h | 4 +-- .../CaloAnalysis/src/SimClusterUtils.cc | 28 ++++++------------- .../RecoParticleFlow/plugins/PFTester.cc | 22 +++++++-------- 5 files changed, 22 insertions(+), 39 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc index 90bb993bf9598..dc3fb81275ffc 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorEDProducer.cc @@ -60,9 +60,7 @@ LCToCPAssociatorEDProducerT::LCToCPAssociatorEDProducerT(const edm::Par label_lc_ = pset.getParameter("label_lc"); std::vector filter_sim_hits = pset.getParameter>("filter_sim_hits"); - - simcluster_utils::check_detids(filter_sim_hits); - detIds_ = simcluster_utils::join_detids(filter_sim_hits); + detIds_ = simcluster_utils::check_and_join_detids(filter_sim_hits); CPCollectionToken_ = consumes(pset.getParameter("label_cp")); LCCollectionToken_ = consumes(label_lc_); diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index 5656b10980420..f42fccdf8b10c 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -58,8 +58,7 @@ LCToSCAssociatorEDProducerT::LCToSCAssociatorEDProducerT(const edm::Par label_scl_ = pset.getParameter("label_scl"); std::vector filter_sim_hits = pset.getParameter>("filter_sim_hits"); - simcluster_utils::check_detids(filter_sim_hits); - detIds_ = simcluster_utils::join_detids(filter_sim_hits); + detIds_ = simcluster_utils::check_and_join_detids(filter_sim_hits); LCCollectionToken_ = consumes(label_lcl_); SCCollectionToken_ = consumes(label_scl_); diff --git a/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h b/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h index 10192ee84a40d..6a2762fe598e8 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h +++ b/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h @@ -20,9 +20,7 @@ std::ostream &operator<<(std::ostream &s, SimCluster const &tp); namespace simcluster_utils { extern const std::unordered_map> DetIdMap; - - std::vector join_detids(const std::vector &dets_v); - void check_detids(const std::vector &dets_v); + std::vector check_and_join_detids(const std::vector &dets_v); } // namespace simcluster_utils #endif diff --git a/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc b/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc index 004dee7406baf..ed1bb0899ea1f 100644 --- a/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc +++ b/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc @@ -8,28 +8,16 @@ namespace simcluster_utils { {"HGCal", std::vector{DetId::HGCalEE, DetId::HGCalHSi, DetId::HGCalHSc}}}; // Build the merged set of detectors from all requested filter strings - std::vector join_detids(const std::vector& dets_v) { - std::vector detIds; - for (const auto& det : dets_v) { + std::vector check_and_join_detids(const std::vector& dets_v) { + std::vector detIds; + for (const auto& det : dets_v) { + if (simcluster_utils::DetIdMap.find(det) == simcluster_utils::DetIdMap.end()) { + throw cms::Exception("Configuration") << "dets_v: unknown value '" << det << "'. " + << "Allowed values are: Ecal, Hcal, HGCal, or empty string."; + } const auto& dets = simcluster_utils::DetIdMap.at(det); - detIds.insert(detIds.end(), dets.begin(), dets.end()); + detIds.insert(detIds.end(), dets.begin(), dets.end()); } return detIds; } - - void check_detids(const std::vector& dets_v) { - std::vector allowed(simcluster_utils::DetIdMap.size()); - int i = 0; - for (auto it = simcluster_utils::DetIdMap.begin(); it != simcluster_utils::DetIdMap.end(); ++it) { - allowed[i] = it->first; // store key - ++i; - } - - for (const auto& det : dets_v) { - if (std::find(allowed.begin(), allowed.end(), det) == allowed.end()) { - throw cms::Exception("Configuration") << "dets_v: unknown value '" << det << "'. " - << "Allowed values are: Ecal, Hcal, HGCal, or empty string."; - } - } - } } // namespace simcluster_utils diff --git a/Validation/RecoParticleFlow/plugins/PFTester.cc b/Validation/RecoParticleFlow/plugins/PFTester.cc index 79fadafa12b14..974c8ee68585d 100644 --- a/Validation/RecoParticleFlow/plugins/PFTester.cc +++ b/Validation/RecoParticleFlow/plugins/PFTester.cc @@ -46,6 +46,7 @@ class PFTesterT : public DQMEDAnalyzer { edm::EDGetTokenT> RecoToSimAssociatorToken_; edm::EDGetTokenT> SimToRecoAssociatorToken_; + std::vector detIds_; std::vector filter_sim_hits_; edm::EDGetTokenT> RecoToCpAssociatorToken_; @@ -236,7 +237,7 @@ PFTesterT::PFTesterT(const edm::ParameterSet& iConfig) h_nSimMatchedToOneReco_.resize(nAssocScoreThresholds_); h_nRecoMatchedToOneSim_.resize(nAssocScoreThresholds_); - simcluster_utils::check_detids(filter_sim_hits_); + detIds_ = simcluster_utils::check_and_join_detids(filter_sim_hits_); } template @@ -552,11 +553,10 @@ void PFTesterT::bookHistograms(DQMStore::IBooker& ibook, template void PFTesterT::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup) { - std::vector detIds = simcluster_utils::join_detids(filter_sim_hits_); DetId::Detector minDet = DetId::Detector(0); //dummy detector id value DetId::Detector maxDet = DetId::Detector(0); //dummy detector id value - if (!detIds.empty()) { - const auto minmaxDet = std::minmax_element(detIds.begin(), detIds.end()); + if (!detIds_.empty()) { + const auto minmaxDet = std::minmax_element(detIds_.begin(), detIds_.end()); minDet = *minmaxDet.first; maxDet = *minmaxDet.second; } @@ -644,9 +644,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (const auto& scRef : caloParticles[cpId].simClusters()) { auto const& sc = *(scRef); SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? sc.hits_and_fractions_view() : sc.hits_and_fractions_view(minDet, maxDet); + detIds_.empty() ? sc.hits_and_fractions_view() : sc.hits_and_fractions_view(minDet, maxDet); SimCluster::HitsAndEnergiesView haeView = - detIds.empty() ? sc.hits_and_energies_view() : sc.hits_and_energies_view(minDet, maxDet); + detIds_.empty() ? sc.hits_and_energies_view() : sc.hits_and_energies_view(minDet, maxDet); // Compute energy of caloParticle as sum of simClusters energies (from SimTrack energy) energySumSimClusters += sc.energy(); @@ -712,9 +712,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { const auto& simCluster = simClusters[simId]; SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); + detIds_.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); SimCluster::HitsAndEnergiesView haeView = - detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); + detIds_.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); double energySumSimHits = simClusterEnergy(haeView); h_SimTrackToSimHitsEnergyFraction_->Fill(energySumSimHits / simCluster.energy()); @@ -966,7 +966,7 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e const auto& simCluster = simClusters[simPairIdx]; SimCluster::HitsAndEnergiesView haeView = - detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); + detIds_.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); #ifdef debug edm::LogPrint("PFTester") << " recoToSimAssoc recoCluster id " << recoId @@ -1041,9 +1041,9 @@ void PFTesterT::analyze(const edm::Event& iEvent, const e for (unsigned int simId = 0; simId < simClusters.size(); ++simId) { const auto& simCluster = simClusters[simId]; SimCluster::HitsAndFractionsView hafView = - detIds.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); + detIds_.empty() ? simCluster.hits_and_fractions_view() : simCluster.hits_and_fractions_view(minDet, maxDet); SimCluster::HitsAndEnergiesView haeView = - detIds.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); + detIds_.empty() ? simCluster.hits_and_energies_view() : simCluster.hits_and_energies_view(minDet, maxDet); double energySumSimHits = simClusterEnergy(haeView); From c64643d3d72f16bbcfd3b655d2d4b7cb7891f55a Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 27 Mar 2026 17:00:43 +0100 Subject: [PATCH 49/64] Ensure enough events are present before attempting the gaussian core fit. wip --- .../scripts/makeHLTPFValidationPlots.py | 38 +++++++++---------- Validation/RecoParticleFlow/scripts/utils.py | 24 ++++++++---- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 87c5d70bcccac..066a76bb36c3a 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -27,9 +27,6 @@ class dotdict(dict): __getattr__ = dict.get __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ - -def debug(mes): - print('### INFO: ' + mes) def rate_errorbar_declutter(plotter, eff, err, yaxmin, frac=0.01): """ @@ -172,7 +169,7 @@ def limits_with_margin(self, mdValues, mdErrors, logY=False, logX=False): def save(self, name): for ext in self.extensions: - debug('Saving ' + name + '.' + ext) + utils.debug('Saving ' + name + '.' + ext) plt.savefig(name + '.' + ext) plt.close() @@ -206,16 +203,19 @@ def plotProject(h, sample_label, era, props, rebin_edges, outname): if props.fit and hproj.GetMean() > 0. and hproj.GetRMS() > 0: gausTF1 = utils.findBestGaussianCoreFit(hproj, meanForRange=0.9, rmsForRange=0.05, quiet=True) - nsigmas = 3 - xfunc = np.linspace(gausTF1.GetParameter(1) - nsigmas*abs(gausTF1.GetParameter(2)), - gausTF1.GetParameter(1) + nsigmas*abs(gausTF1.GetParameter(2))) - yfunc = np.array([gausTF1.Eval(xi) for xi in xfunc]) - if not (gausTF1.GetParameter(1) < hproj.GetBinLowEdge(1) - or gausTF1.GetParameter(1) > hproj.GetBinLowEdge(hproj.GetNbinsX())): - plotter.ax.plot(xfunc, yfunc, color=line.get_edgecolor(), - linewidth=1., linestyle='-') - fit_params[(low,high)] = (gausTF1.GetParameter(1), gausTF1.GetParameter(2), - gausTF1.GetParError(1), gausTF1.GetParError(2)) + if gausTF1 is None: + fit_params = None + else: + nsigmas = 3 + xfunc = np.linspace(gausTF1.GetParameter(1) - nsigmas*abs(gausTF1.GetParameter(2)), + gausTF1.GetParameter(1) + nsigmas*abs(gausTF1.GetParameter(2))) + yfunc = np.array([gausTF1.Eval(xi) for xi in xfunc]) + if not (gausTF1.GetParameter(1) < hproj.GetBinLowEdge(1) + or gausTF1.GetParameter(1) > hproj.GetBinLowEdge(hproj.GetNbinsX())): + plotter.ax.plot(xfunc, yfunc, color=line.get_edgecolor(), + linewidth=1., linestyle='-') + fit_params[(low,high)] = (gausTF1.GetParameter(1), gausTF1.GetParameter(2), + gausTF1.GetParError(1), gausTF1.GetParError(2)) plotter.limits_with_margin(valuesList, errorsList, logY=props.logy) @@ -553,7 +553,7 @@ def __call__(self, parser, namespace, values, option_string=None): afile = ROOT.TFile.Open(args.file) utils.checkRootDir(afile, dqm_dir) - debug('Start caching PFCluster histograms...') + utils.debug('Start caching PFCluster histograms...') subdirs = [] cached_histos = {} directory = afile.GetDirectory(dqm_dir) @@ -568,7 +568,7 @@ def __call__(self, parser, namespace, values, option_string=None): else: name = key.GetName() cached_histos[name] = key.ReadObj() - debug(' ...done.') + utils.debug(' ...done.') # create and setup folders for subdir in subdirs: @@ -878,7 +878,7 @@ def __call__(self, parser, namespace, values, option_string=None): fitpars = plotProject(root_hist, args.sample_label, args.era, props, rebin_edges=props.rebin, outname=os.path.join(args.odir, name + '_Projected')) - if props.fit: + if props.fit and fitpars is not None: n_bins = len(fitpars) xmin = min(low for low, _ in fitpars.keys()) xmax = max(high for _, high in fitpars.keys()) @@ -984,7 +984,7 @@ def __call__(self, parser, namespace, values, option_string=None): afile = ROOT.TFile.Open(args.file) utils.checkRootDir(afile, dqm_dir) - debug('Start caching PFCluster histograms...') + utils.debug('Start caching PFCluster histograms...') subdirs = [] cached_histos = {} directory = afile.GetDirectory(dqm_dir) @@ -999,7 +999,7 @@ def __call__(self, parser, namespace, values, option_string=None): else: name = key.GetName() cached_histos[name] = key.ReadObj() - debug(' ...done.') + utils.debug(' ...done.') vars1D = { 'CP_simToRecoShEnF': dict(xtitle='SimToReco Shared Energy Fraction', ytitle='# CaloParticles', var='CaloParticles', logy=False), diff --git a/Validation/RecoParticleFlow/scripts/utils.py b/Validation/RecoParticleFlow/scripts/utils.py index e15c2b3227a78..ffa8b2ec89fee 100644 --- a/Validation/RecoParticleFlow/scripts/utils.py +++ b/Validation/RecoParticleFlow/scripts/utils.py @@ -21,11 +21,18 @@ def createIndexPHP(src, dest): php_file = os.path.join(src, 'index.php') if os.path.exists(php_file) and not os.path.exists(os.path.join(src, 'index.php')): os.system(f'cp {php_file} {dest}') - + +def debug(mes): + print('### INFO: ' + mes) + def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1): """ - The meanForRange and rmsForRange are initial estimates. + Implementation of a gaussian core fit. """ + if histo.Integral() < 50: + debug(' [findBestGaussianCoreFit] There is not enough data for running the fit.') + return None + Xmax = histo.GetXaxis().GetBinCenter(histo.GetMaximumBin()) gausTF1 = ROOT.TF1() @@ -36,6 +43,7 @@ def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1) PvalueBest = 0. rms_step_minus = 2.2 + # the meanForRange and rmsForRange are initial estimates. RangeLowBest = meanForRange - rms_step_minus*rmsForRange RangeUpBest = meanForRange + rms_step_minus*rmsForRange @@ -67,9 +75,9 @@ def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1) meanForRange = gausTF1.GetParameter(1) if not quiet: - print(f"\nFitting range used: [{meanForRange} - {rms_step_minus} sigma, {meanForRange} + {rms_step_plus} sigma ] ") - print(f"ChiSquare = {ChiSquare}, NDF = {ndf}, Prob = {Pvalue}, Best Prob so far = {PvalueBest}") - print(f"Sigma limit = {range_max_dist}") + debug(f"[findBestGaussianCoreFit] \nFitting range used: [{meanForRange} - {rms_step_minus} sigma, {meanForRange} + {rms_step_plus} sigma ] ") + debug(f"ChiSquare = {ChiSquare}, NDF = {ndf}, Prob = {Pvalue}, Best Prob so far = {PvalueBest}") + debug(f"Sigma limit = {range_max_dist}") rms_step_plus -= sigma_step @@ -79,9 +87,9 @@ def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1) histo.Fit("gaus", "0Q", "0", RangeLowBest, RangeUpBest) else: histo.Fit("gaus","0","0", RangeLowBest, RangeUpBest) - print("Fit found!") - print(f"Final fitting range used: [{meanForRange} - {StepMinusBest} rms(WHF), {meanForRange} + {StepPlusBest} rms(WHF) ]") - print(f"ChiSquare = {ChiSquareBest}, NDF = {ndfBest}, Prob = {PvalueBest}\n\n") + debug("[findBestGaussianCoreFit] Fit found!") + debug(f"Final fitting range used: [{meanForRange} - {StepMinusBest} rms(WHF), {meanForRange} + {StepPlusBest} rms(WHF) ]") + debug(f"ChiSquare = {ChiSquareBest}, NDF = {ndfBest}, Prob = {PvalueBest}\n\n") return histo.GetListOfFunctions().FindObject("gaus") From 0136d02a413679548db1dfd7732c11018f76460d Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 27 Mar 2026 22:19:34 +0100 Subject: [PATCH 50/64] Handle fit crashes due to low statistics. --- .../scripts/makeHLTPFValidationPlots.py | 65 +++++++++---------- Validation/RecoParticleFlow/scripts/utils.py | 31 ++++++--- 2 files changed, 55 insertions(+), 41 deletions(-) diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 066a76bb36c3a..716697b5da5fb 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -182,7 +182,7 @@ def plotProject(h, sample_label, era, props, rebin_edges, outname): valuesList, errorsList = [], [] fit_params = {} if props.fit else None plotter = Plotter(sample_label, grid_color=None, era=era) - + for ibin, (low, high) in enumerate(zip(rebin_edges[:-1],rebin_edges[1:])): hproj = h.ProjectionY(h.GetName() + "_proj" + str(ibin), h.GetXaxis().FindBin(low), h.GetXaxis().FindBin(high), "e") @@ -201,10 +201,10 @@ def plotProject(h, sample_label, era, props, rebin_edges, outname): color=line.get_edgecolor(), fmt='s', label=label, **errorbar_kwargs) - if props.fit and hproj.GetMean() > 0. and hproj.GetRMS() > 0: + if props.fit: gausTF1 = utils.findBestGaussianCoreFit(hproj, meanForRange=0.9, rmsForRange=0.05, quiet=True) if gausTF1 is None: - fit_params = None + fit_params[(low,high)] = (None, None, None, None) else: nsigmas = 3 xfunc = np.linspace(gausTF1.GetParameter(1) - nsigmas*abs(gausTF1.GetParameter(2)), @@ -238,11 +238,9 @@ def plotOverlay(subdirs, cached_histos, name, match_by_score, sample_label, era, plotter = Plotter(sample_label, grid_color=None, era=era) for sub in subdirs: root_hist = cached_histos[f"{sub}/{name}"] - if props.rebin is not None: root_hist = root_hist.Clone(f"{name}" + "_clone") root_hist.SetDirectory(0) # detach from file - if isinstance(props.rebin, (int, float)): root_hist = root_hist.Rebin(int(props.rebin), f"{name}" + "_rebin") elif hasattr(props.rebin, '__iter__'): @@ -268,8 +266,6 @@ def plotOverlay(subdirs, cached_histos, name, match_by_score, sample_label, era, fmt='s', label=sublabel, **errorbar_kwargs) plotter.limits(y=(0,1.1), x=(bin_edges[0], bin_edges[-1])) else: - # if "Response" in name: - # plotter.limits(y=(0, 2.0)) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) @@ -853,7 +849,6 @@ def __call__(self, parser, namespace, values, option_string=None): **{f'{subdir}/ResponseE_Eta': InputArgs( xtitle=titles['response'], ytitle='# Clusters', var=r'$\eta$', - # rebin=(-1.5, -1.3, -1., -0.75, -0.5, -0.25, 0., 0.25, 0.5, 0.75, 1., 1.3, 1.5) rebin=(-1.5, -0.75, 0., 0.75, 1.5) ) for subdir in subdirs}, **{f'{subdir}/ResponseE_Phi': @@ -873,29 +868,33 @@ def __call__(self, parser, namespace, values, option_string=None): ), } + fitpars = {} for name, props in vars2DProjection.items(): root_hist = cached_histos[f"{name}"] - fitpars = plotProject(root_hist, args.sample_label, args.era, props, rebin_edges=props.rebin, - outname=os.path.join(args.odir, name + '_Projected')) + fp = plotProject(root_hist, args.sample_label, args.era, props, rebin_edges=props.rebin, + outname=os.path.join(args.odir, name + '_Projected')) + fitpars[name] = fp - if props.fit and fitpars is not None: - n_bins = len(fitpars) - xmin = min(low for low, _ in fitpars.keys()) - xmax = max(high for _, high in fitpars.keys()) + if props.fit and fp is not None: + n_bins = len(fp) + xmin = min(low for low, _ in fp.keys()) + xmax = max(high for _, high in fp.keys()) fitstr = 'FromFit{}_'+os.path.basename(name) fitdir = os.path.dirname(name) + '/' + fitstr cached_histos[fitdir.format('Mean')] = ROOT.TH1F(fitdir.format('Mean').replace('/','_'), fitstr.format('Mean'), n_bins, xmin, xmax) cached_histos[fitdir.format('Width')] = ROOT.TH1F(fitdir.format('Width').replace('/','_'), fitstr.format('Width'), n_bins, xmin, xmax) - for i, ((low, high), (mean, width, mean_err, width_err)) in enumerate(fitpars.items(), 1): - cached_histos[fitdir.format('Mean')].SetBinContent(i, mean) - cached_histos[fitdir.format('Mean')].SetBinError(i, mean_err) - cached_histos[fitdir.format('Width')].SetBinContent(i, width) - cached_histos[fitdir.format('Width')].SetBinError(i, width_err) - + for i, ((low, high), (mean, width, mean_err, width_err)) in enumerate(fp.items(), 1): + if mean is not None: + cached_histos[fitdir.format('Mean')].SetBinContent(i, mean) + cached_histos[fitdir.format('Mean')].SetBinError(i, mean_err) + cached_histos[fitdir.format('Width')].SetBinContent(i, width) + cached_histos[fitdir.format('Width')].SetBinError(i, width_err) + for name, props in vars2DProjection.items(): - if props.fit: + if props.fit and fitpars[name] is not None: + fitstr = 'FromFit{}_'+os.path.basename(name) props.xtitle = 'Energy [GeV]' props.ytitle = 'Response' plotOverlay(subdirs, cached_histos, fitstr.format('Mean'), args.match_by_score, args.sample_label, args.era, props, outdir=args.odir) @@ -960,9 +959,9 @@ def __call__(self, parser, namespace, values, option_string=None): **{f'{subdir}/ResponseE_Phi': dict(ytitle=titles['response'], var='# Clusters', xtitle=r'$\phi$') for subdir in subdirs}, }) - for name, props in vars2D.items(): - root_hist = cached_histos[f"{name}"] - plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) + # for name, props in vars2D.items(): + # root_hist = cached_histos[f"{name}"] + # plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) vars1D = { 'simToRecoShEnF': dict(xtitle='SimToReco Shared Energy Fraction', ytitle='# SimClusters', var='SimClusters', logy=False), @@ -970,9 +969,9 @@ def __call__(self, parser, namespace, values, option_string=None): 'recoToSimScore': dict(xtitle='RecoToSim Score', ytitle='# SimClusters', var='SimClusters', logy=False), } - for name, props in vars1D.items(): - root_hist = cached_histos[f"{name}"] - plot1D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) + # for name, props in vars1D.items(): + # root_hist = cached_histos[f"{name}"] + # plot1D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) ################################################################################## # Temporary hack to access CaloParticle histrograms @@ -1007,14 +1006,14 @@ def __call__(self, parser, namespace, values, option_string=None): 'CP_recoToSimScore': dict(xtitle='RecoToSim Score', ytitle='# CaloParticles', var='CaloParticles', logy=False), } - for name, props in vars1D.items(): - root_hist = cached_histos[f"{name}"] - plot1D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) + # for name, props in vars1D.items(): + # root_hist = cached_histos[f"{name}"] + # plot1D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) vars2D = { 'CP_simToRecoShEnF_Score': dict(ytitle='SimToReco Score', var='# CaloParticles', xtitle='SimToReco Shared Energy Fraction', logz=True), } - for name, props in vars2D.items(): - root_hist = cached_histos[f"{name}"] - plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) + # for name, props in vars2D.items(): + # root_hist = cached_histos[f"{name}"] + # plot2D(root_hist, args.sample_label, args.era, props, outname=os.path.join(args.odir, name)) diff --git a/Validation/RecoParticleFlow/scripts/utils.py b/Validation/RecoParticleFlow/scripts/utils.py index ffa8b2ec89fee..77caec2e8b590 100644 --- a/Validation/RecoParticleFlow/scripts/utils.py +++ b/Validation/RecoParticleFlow/scripts/utils.py @@ -28,22 +28,23 @@ def debug(mes): def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1): """ Implementation of a gaussian core fit. + The meanForRange and rmsForRange are initial estimates for the position and width of the gaussian. + The peak must lie within the initial estimates, otherwise the fit will fail. """ - if histo.Integral() < 50: - debug(' [findBestGaussianCoreFit] There is not enough data for running the fit.') + nDataMin = 60 + if histo.Integral(1, histo.GetNbinsX()) < nDataMin: + debug(f'[findBestGaussianCoreFit] The fit needs at least {nDataMin} data elements. Skipping.') return None Xmax = histo.GetXaxis().GetBinCenter(histo.GetMaximumBin()) gausTF1 = ROOT.TF1() - Pvalue = 0. RangeLow = histo.GetBinLowEdge(2) RangeUp = histo.GetBinLowEdge(histo.GetNbinsX()) PvalueBest = 0. rms_step_minus = 2.2 - # the meanForRange and rmsForRange are initial estimates. RangeLowBest = meanForRange - rms_step_minus*rmsForRange RangeUpBest = meanForRange + rms_step_minus*rmsForRange @@ -51,14 +52,28 @@ def findBestGaussianCoreFit(histo, quiet=True, meanForRange=1., rmsForRange=0.1) sigma_step = 0.1 StepMinusBest, StepPlusBest, ChiSquareBest, ndfBest = (None for _ in range(4)) - while rms_step_minus>range_max_dist: + stop = False + while rms_step_minus>range_max_dist and not stop: RangeLow = meanForRange - rms_step_minus*rmsForRange rms_step_plus = rms_step_minus - while rms_step_plus>range_max_dist: - RangeUp = meanForRange + rms_step_plus*rmsForRange - histo.Fit("gaus", "0Q" if quiet else "0", "0", RangeLow, RangeUp) + while rms_step_plus>range_max_dist and not stop: + RangeUp = meanForRange + rms_step_plus*rmsForRange + + # if there is not enough data in the initial region (first iteration), the fit will never converge + if PvalueBest==0 and histo.Integral(histo.FindBin(RangeLow), histo.FindBin(RangeUp)) < nDataMin / 2: + debug(f'[findBestGaussianCoreFit] The fit needs at least {nDataMin / 2} data elements in the fit region. Skipping.') + debug(f'You likely provided wrong initial estimates (the histogram has {histo.Integral(1, histo.GetNbinsX())} data elements). Skipping.') + return None + if histo.FindBin(RangeUp) - histo.FindBin(RangeLow) < 2: + debug(f'[findBestGaussianCoreFit] The gaussian fit needs at least two bins to run.') + if PvalueBest==0: # first iteration + return None + else: + stop = True + continue + histo.Fit("gaus", "0Q" if quiet else "0", "0", RangeLow, RangeUp) gausTF1 = histo.GetListOfFunctions().FindObject("gaus") ChiSquare = gausTF1.GetChisquare() ndf = gausTF1.GetNDF() From 8365046aec7d8faae008d2da27c62b011572dfa8 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Sat, 28 Mar 2026 00:14:07 +0100 Subject: [PATCH 51/64] Clamp scores between 0 and 1. --- .../plugins/LCToCPAssociatorByEnergyScoreImpl.cc | 8 ++++++++ .../plugins/LCToSCAssociatorByEnergyScoreImpl.cc | 8 ++++++++ .../HGCalValidation/python/HLTHGCalValidator_cff.py | 2 +- Validation/RecoParticleFlow/python/hltPFValidation_cfi.py | 2 +- .../RecoParticleFlow/scripts/makeHLTPFValidationPlots.py | 2 -- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc index 4228e143a0153..ba60645056fd7 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToCPAssociatorByEnergyScoreImpl.cc @@ -436,6 +436,10 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti LogDebug("LCToCPAssociatorByEnergyScoreImplT") << "layerCluster Id: \t" << lcId << "\tCP id:\t-1 " << "\t score \t-1\n"; #endif + // avoid floating point rounding errors; force score to lie between 0 and 1 + for (auto& cpPair : cpsInLayerCluster[lcId]) { + cpPair.second = std::clamp(cpPair.second, 0.f, 1.f); + } } // End of loop over LayerClusters // Compute the CaloParticle-To-LayerCluster score @@ -538,6 +542,10 @@ ticl::association LCToCPAssociatorByEnergyScoreImplT::makeConnecti << (lcPair.second.first / CPenergy) << "\n"; } #endif + // avoid floating point rounding errors; force score to lie between 0 and 1 + for (auto& lcPair : cPOnLayer[cpId][layerId].layerClusterIdToEnergyAndScore) { + lcPair.second.second = std::clamp(lcPair.second.second, 0.f, 1.f); + } } // End of loop over layers } // End of loop over CaloParticles diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index 55a748c6c3051..fd7b8f5d55a3d 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -447,6 +447,10 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti << "\t score \t-1" << "\n"; #endif + // avoid floating point rounding errors; force score to lie between 0 and 1 + for (auto& scPair : scsInLayerCluster[lcId]) { + scPair.second = std::clamp(scPair.second, 0.f, 1.f); + } } // End of loop over LayerClusters // Compute the SimCluster-To-LayerCluster score @@ -553,6 +557,10 @@ ticl::association LCToSCAssociatorByEnergyScoreImplT::makeConnecti << (lcPair.second.first / SCenergy) << "\n"; } #endif + // avoid floating point rounding errors; force score to lie between 0 and 1 + for (auto& lcPair : lcsInSimCluster[scId][layerId].layerClusterIdToEnergyAndScore) { + lcPair.second.second = std::clamp(lcPair.second.second, 0.f, 1.f); + } } // End of loop over layers } // End of loop over SimClusters diff --git a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py index ab23663c5a7e8..018fb0a030c0a 100644 --- a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py +++ b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py @@ -56,7 +56,7 @@ ClusterSimClusterAssociator = cms.InputTag("hltBarrelLayerClusterSimClusterAssociationProducer"), ClusterCaloParticleAssociator = cms.InputTag("hltBarrelLayerClusterCaloParticleAssociationProducer"), outFolder = cms.string('HLT/TiclBarrel'), - assocScoreThresholds = cms.vdouble(1.1, 0.5, 0.1), + assocScoreThresholds = cms.vdouble(1., 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.), diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 45c11df474c1d..c2afd05efd36f 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -38,7 +38,7 @@ ClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), filter_sim_hits = _filter_sim_hits, outFolder = cms.string('HLT/ParticleFlow'), - assocScoreThresholds = cms.vdouble(1.1, 0.5, 0.1), + assocScoreThresholds = cms.vdouble(1., 0.5, 0.1), doMatchByScore = cms.bool(True), enFracCut = cms.double(0.), ptCut = cms.double(0.), diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 716697b5da5fb..6e68e368373ee 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -258,7 +258,6 @@ def plotOverlay(subdirs, cached_histos, name, match_by_score, sample_label, era, baseline=None, color=next(colors_iter)) sublabel = re.sub(pattern, replacement, sub) - if sublabel == f'{matching} = 1.1': sublabel = f'{matching} = 1.0' # Temporary fix if any(x in name for x in ('Eff', 'Fake', 'Split', 'Merge')): eff_filt, (err_filt_lo, err_filt_hi), up_error, transform = rate_errorbar_declutter(plotter, values, errors, 0) plotter.ax.errorbar(bin_centers, values, xerr=None, yerr=[err_filt_lo,err_filt_hi], @@ -318,7 +317,6 @@ def plotOverlayRatio(subdirs, cached_histos, num, den, match_by_score, sample_la baseline=None, color=next(colors_iter)) sublabel = re.sub(pattern, replacement, sub) - if sublabel == f'{matching} = 1.1': sublabel = f'{matching} = 1.0' # Temporary fix plotter.ax.errorbar(bin_centers, ratio_vals, xerr=None, yerr=ratio_errors, color=line.get_edgecolor(), fmt='s', label=sublabel, **errorbar_kwargs) From 2566bc74434484f52407a0792ccd8e9a3c7d51ee Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Sat, 28 Mar 2026 00:29:17 +0100 Subject: [PATCH 52/64] Remove unused fake vs pt + fix eff vs en label. --- SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc | 14 +++++++------- .../scripts/makeHLTPFValidationPlots.py | 10 ++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc b/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc index ed1bb0899ea1f..9d791c3a79e9b 100644 --- a/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc +++ b/SimDataFormats/CaloAnalysis/src/SimClusterUtils.cc @@ -9,14 +9,14 @@ namespace simcluster_utils { // Build the merged set of detectors from all requested filter strings std::vector check_and_join_detids(const std::vector& dets_v) { - std::vector detIds; - for (const auto& det : dets_v) { - if (simcluster_utils::DetIdMap.find(det) == simcluster_utils::DetIdMap.end()) { - throw cms::Exception("Configuration") << "dets_v: unknown value '" << det << "'. " - << "Allowed values are: Ecal, Hcal, HGCal, or empty string."; - } + std::vector detIds; + for (const auto& det : dets_v) { + if (simcluster_utils::DetIdMap.find(det) == simcluster_utils::DetIdMap.end()) { + throw cms::Exception("Configuration") << "dets_v: unknown value '" << det << "'. " + << "Allowed values are: Ecal, Hcal, HGCal, or empty string."; + } const auto& dets = simcluster_utils::DetIdMap.at(det); - detIds.insert(detIds.end(), dets.begin(), dets.end()); + detIds.insert(detIds.end(), dets.begin(), dets.end()); } return detIds; } diff --git a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py index 6e68e368373ee..e749eb40ce246 100755 --- a/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py +++ b/Validation/RecoParticleFlow/scripts/makeHLTPFValidationPlots.py @@ -671,13 +671,7 @@ def __call__(self, parser, namespace, values, option_string=None): ratio=f'{subdir}/Fake_vs_En', den=f'RecoClustersEn', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersEn', legnum='Matched RecoClusters', - xtitle='Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=True, - ), - f'{subdir}/Fake_vs_Pt': InputArgs( - ratio=f'{subdir}/Fake_vs_Pt', - den=f'RecoClustersPt', legden='RecoClusters', - num=f'{subdir}/RecoClustersMatchedSimClustersPt', legnum='Matched RecoClusters', - xtitle=r'$p_{T}$ [GeV]', ytitle=nPFClustersLabel, rebin=4, + xtitle='RecoCluster Energy [GeV]', ytitle=nPFClustersLabel, rebin=4, logy=True, ), f'{subdir}/Fake_vs_Eta': InputArgs( ratio=f'{subdir}/Fake_vs_Eta', @@ -692,7 +686,7 @@ def __call__(self, parser, namespace, values, option_string=None): xtitle=r'$\phi$', ytitle=nPFClustersLabel, ), f'{subdir}/Fake_vs_Mult': InputArgs( - ratio=f'{subdir}/Fake_vs_Mult', + ratio=f'{subdir}/Fake_vs_Mult', den=f'RecoClustersMult', legden='RecoClusters', num=f'{subdir}/RecoClustersMatchedSimClustersMult', legnum='Matched RecoClusters', xtitle='Multiplicity', ytitle=nPFClustersLabel, rebin=4, From df1bce2264604ba35b8126abcee9b9c7c284a73e Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Mon, 30 Mar 2026 11:39:31 +0200 Subject: [PATCH 53/64] Remove superfluous includes. --- .../plugins/LCToSCAssociatorByEnergyScoreImpl.cc | 2 -- .../plugins/LCToSCAssociatorEDProducer.cc | 3 --- SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h | 1 - Validation/RecoParticleFlow/python/hltPFValidation_cfi.py | 8 ++++---- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc index fd7b8f5d55a3d..e5f636fb49d7d 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorByEnergyScoreImpl.cc @@ -5,8 +5,6 @@ #include "SimDataFormats/CaloAnalysis/interface/SimCluster.h" #include "DataFormats/CaloRecHit/interface/CaloCluster.h" -#include - template LCToSCAssociatorByEnergyScoreImplT::LCToSCAssociatorByEnergyScoreImplT( edm::EDProductGetter const& productGetter, diff --git a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc index f42fccdf8b10c..5bc57c168b1b6 100644 --- a/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc +++ b/SimCalorimetry/HGCalAssociatorProducers/plugins/LCToSCAssociatorEDProducer.cc @@ -15,7 +15,6 @@ #include "FWCore/Framework/interface/ESHandle.h" #include "FWCore/Framework/interface/Event.h" #include "FWCore/Framework/interface/MakerMacros.h" -#include "FWCore/ParameterSet/interface/allowedValues.h" #include "FWCore/Framework/interface/global/EDProducer.h" #include "FWCore/MessageLogger/interface/MessageLogger.h" #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" @@ -23,8 +22,6 @@ #include "FWCore/Utilities/interface/EDGetToken.h" #include "SimDataFormats/Associations/interface/LayerClusterToSimClusterAssociator.h" -#include - // // class declaration // diff --git a/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h b/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h index 6a2762fe598e8..cb6c8931da038 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h +++ b/SimDataFormats/CaloAnalysis/interface/SimClusterFwd.h @@ -5,7 +5,6 @@ #include "DataFormats/Common/interface/RefVector.h" #include "DataFormats/DetId/interface/DetId.h" #include -#include class SimCluster; std::ostream &operator<<(std::ostream &s, SimCluster const &tp); diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index c2afd05efd36f..54c58074747e8 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -11,7 +11,7 @@ hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), label_lcl = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - label_scl = cms.InputTag("mix","MergedCaloTruth"), + label_scl = cms.InputTag("mix", "MergedCaloTruth"), filter_sim_hits = _filter_sim_hits ) @@ -24,7 +24,7 @@ hltPFClusterCaloParticleAssociationProducerECAL = cms.EDProducer("PCToCPAssociatorEDProducer", associator = cms.InputTag("hltPFCpAssocByEnergyScoreProducer"), label_lc = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - label_cp = cms.InputTag("mix","MergedCaloTruth"), + label_cp = cms.InputTag("mix", "MergedCaloTruth"), filter_sim_hits = _filter_sim_hits ) @@ -32,8 +32,8 @@ PFCand = cms.InputTag("hltParticleFlow"), Rechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), RecoCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - SimCluster = cms.InputTag("mix","MergedCaloTruth"), - CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + SimCluster = cms.InputTag("mix", "MergedCaloTruth"), + CaloParticle = cms.InputTag("mix", "MergedCaloTruth"), ClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), ClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), filter_sim_hits = _filter_sim_hits, From b92ba2e8ad21186315f5c2894d622e6932303bac Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Mon, 30 Mar 2026 19:36:40 +0200 Subject: [PATCH 54/64] Avoid setting the sim cluster energy twice. --- .../CaloAnalysis/interface/MtdSimCluster.h | 14 ++++--- .../CaloAnalysis/interface/SimCluster.h | 40 +++++++------------ .../plugins/CaloTruthAccumulator.cc | 9 ++--- .../plugins/MtdTruthAccumulator.cc | 16 ++++---- .../plugins/PreMixingCaloParticleWorker.cc | 10 ++--- 5 files changed, 38 insertions(+), 51 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/MtdSimCluster.h b/SimDataFormats/CaloAnalysis/interface/MtdSimCluster.h index b7db1f4afaf60..b39788cb1f451 100644 --- a/SimDataFormats/CaloAnalysis/interface/MtdSimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/MtdSimCluster.h @@ -27,9 +27,10 @@ class MtdSimCluster : public SimCluster { } /** @brief add hit with fraction */ - void addHitAndFraction(uint64_t hit, float fraction) { + void addHitAndEnergy(uint64_t hit, float energy) { mtdHits_.emplace_back(hit); - fractions_.emplace_back(fraction); + energies_.emplace_back(energy); + hitsFinalized_ = false; } /** @brief add hit position*/ @@ -58,9 +59,10 @@ class MtdSimCluster : public SimCluster { } /** @brief clear the hits and fractions list */ - void clearHitsAndFractions() { + void clearHitsAndEnergies() { std::vector().swap(mtdHits_); - std::vector().swap(fractions_); + std::vector().swap(energies_); + hitsFinalized_ = false; } /** @brief Returns list of hit IDs and times for this SimCluster */ @@ -104,8 +106,8 @@ class MtdSimCluster : public SimCluster { void clearHitsPosition() { std::vector().swap(positions_); } void clear() { - clearHitsAndFractions(); - clearHitsEnergy(); + clearHitsAndEnergies(); + clearFractions(); clearHitsTime(); clearHitsPosition(); } diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index 9c0ff22bc0d01..41b902c3decbe 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -252,16 +252,16 @@ class SimCluster { /** @brief Gives the total number of SimHits, in the cluster */ int numberOfRecHits() const { return hits_.size(); } - /** @brief add rechit with fraction */ - void addRecHitAndFraction(uint32_t hit, float fraction) { + /** @brief add rechit with energy */ + void addRecHitAndEnergy(uint32_t hit, float energy) { hits_.emplace_back(hit); - fractions_.emplace_back(fraction); + energies_.emplace_back(energy); hitsFinalized_ = false; } - /** @brief add rechit energy */ - void addHitEnergy(float energy) { - energies_.emplace_back(energy); + /** @brief add rechit fraction */ + void addHitFraction(float fraction) { + fractions_.emplace_back(fraction); hitsFinalized_ = false; } @@ -298,14 +298,14 @@ class SimCluster { return result; } - void clearHitsAndFractions() { + void clearHitsAndEnergies() { std::vector().swap(hits_); - std::vector().swap(fractions_); + std::vector().swap(energies_); hitsFinalized_ = false; } - void clearHitsEnergy() { - std::vector().swap(energies_); + void clearFractions() { + std::vector().swap(fractions_); hitsFinalized_ = false; } @@ -323,10 +323,7 @@ class SimCluster { // -------------------------------------------------------------------------- void finalizeHits() { // Keep your original implicit invariant: - assert(hits_.size() == fractions_.size() && !hits_.empty()); - // Energies are optional but if present must align. - if (!energies_.empty()) - assert(energies_.size() == hits_.size()); + assert(hits_.size() == fractions_.size() && hits_.size() == energies_.size() && !hits_.empty()); // Already finalized? keep it cheap and idempotent. if (hitsFinalized_) @@ -345,8 +342,7 @@ class SimCluster { applyPermutation_(hits_, order); applyPermutation_(fractions_, order); - if (!energies_.empty()) - applyPermutation_(energies_, order); + applyPermutation_(energies_, order); buildDetRanges_(); @@ -381,27 +377,23 @@ class SimCluster { HitsAndEnergiesView hits_and_energies_view() const { assertFinalized_(); - assertEnergies_(); return HitsAndEnergiesView{{hits_, energies_}}; } HitsAndEnergiesView hits_and_energies_view(DetId::Detector det) const { assertFinalized_(); - assertEnergies_(); auto [b, e] = detRanges_[detIndex_(det)]; return HitsAndEnergiesView{{{hits_.data() + b, e - b}, {energies_.data() + b, e - b}}}; } HitsAndEnergiesView hits_and_energies_view(DetId::Detector det, int subdetId) const { assertFinalized_(); - assertEnergies_(); auto [bb, ee] = subdetRange_(det, subdetId); return HitsAndEnergiesView{{{hits_.data() + bb, ee - bb}, {energies_.data() + bb, ee - bb}}}; } HitsAndEnergiesView hits_and_energies_view(DetId::Detector detIdMin, DetId::Detector detIdMax) const { assertFinalized_(); - assertEnergies_(); auto [begin, end] = detMinMaxRange_(detIdMin, detIdMax); return HitsAndEnergiesView{{{hits_.data() + begin, end - begin}, {energies_.data() + begin, end - begin}}}; } @@ -422,10 +414,11 @@ class SimCluster { /// Ref to GenParticle (in case the SimCluster is created from the entire GenParticle). Usually either empty or length 1 reco::GenParticleRefVector genParticles_; + bool hitsFinalized_{false}; + private: static constexpr size_t kMaxDetectors_ = 32; // Probably 16 could be enough - bool hitsFinalized_{false}; std::array, kMaxDetectors_> detRanges_{}; // [begin,end) per detector static size_t detIndex_(DetId::Detector det) { @@ -438,11 +431,6 @@ class SimCluster { assert(hitsFinalized_ && "SimCluster: hits not finalized. Call finalizeHits() in the producer before persisting."); } - void assertEnergies_() const { - assert(!energies_.empty() && - "SimCluster: energies is empty; populate it with addHitEnergy() before calling this view."); - } - // Returns the [begin, end) index range within hits_ for a det+subdet pair. std::pair subdetRange_(DetId::Detector det, int subdetId) const { auto [b, e] = detRanges_[detIndex_(det)]; diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index 5d04ab1b34b5c..28329fa196ee7 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -357,8 +357,7 @@ namespace { void doEndCluster(SimClassNameT &cluster) { for (auto const &hit_and_energy : acc_energy) { - cluster.addRecHitAndFraction(hit_and_energy.first, hit_and_energy.second); - cluster.addHitEnergy(hit_and_energy.second); + cluster.addRecHitAndEnergy(hit_and_energy.first, hit_and_energy.second); } acc_energy.clear(); } @@ -704,8 +703,7 @@ namespace { std::unordered_map const &detIdToTotalSimEnergy) { for (auto &sc : simClusters) { auto hitsAndEnergies = sc.hits_and_fractions(); - sc.clearHitsAndFractions(); - sc.clearHitsEnergy(); + sc.clearFractions(); for (auto &hAndE : hitsAndEnergies) { const float totalenergy = detIdToTotalSimEnergy.at(hAndE.first); float fraction = 0.; @@ -714,8 +712,7 @@ namespace { else edm::LogWarning("CaloTruthAccumulator") << "TotalSimEnergy for hit " << hAndE.first << " is 0! The fraction for this hit cannot be computed."; - sc.addRecHitAndFraction(hAndE.first, fraction); - sc.addHitEnergy(hAndE.second); + sc.addHitFraction(fraction); } sc.finalizeHits(); } diff --git a/SimGeneral/CaloAnalysis/plugins/MtdTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/MtdTruthAccumulator.cc index 930a1b5daec5e..6abee07c6c9e1 100644 --- a/SimGeneral/CaloAnalysis/plugins/MtdTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/MtdTruthAccumulator.cc @@ -202,8 +202,8 @@ namespace { acc_energy[hit_and_energy.first] += std::get<0>(hit_and_energy.second); } for (auto const &hit_and_energy : acc_energy) { - simcluster.addHitAndFraction(hit_and_energy.first, hit_and_energy.second); - simcluster.addHitEnergy(hit_and_energy.second); + simcluster.addHitAndEnergy(hit_and_energy.first, hit_and_energy.second); + simcluster.addHitFraction(hit_and_energy.second); simcluster.addHitTime(std::get<1>( simTrackDetIdMap_[simcluster.g4Tracks()[0].trackId() + offset * (static_cast(PSimHit::k_tidOffset))][hit_and_energy.first])); @@ -368,8 +368,8 @@ void MtdTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup const } else { for (auto &sc : *(output_.pSimClusters)) { auto hitsAndEnergies = sc.hits_and_fractions(); - sc.clearHitsAndFractions(); - sc.clearHitsEnergy(); + sc.clearHitsAndEnergies(); + sc.clearFractions(); for (auto &hAndE : hitsAndEnergies) { const float totalenergy = m_detIdToTotalSimEnergy[hAndE.first]; float fraction = 0.; @@ -378,8 +378,8 @@ void MtdTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup const else edm::LogWarning(messageCategory_) << "TotalSimEnergy for hit " << hAndE.first << " is 0! The fraction for this hit cannot be computed."; - sc.addHitAndFraction(hAndE.first, fraction); - sc.addHitEnergy(hAndE.second); + sc.addHitAndEnergy(hAndE.first, hAndE.second); + sc.addHitFraction(fraction); } } } @@ -438,8 +438,8 @@ void MtdTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup const float SimLCx = 0., SimLCy = 0., SimLCz = 0.; auto push_back_hit = [&](const int &ind) { - tmpLC.addHitAndFraction(hAndF[ind].first, hAndF[ind].second); - tmpLC.addHitEnergy(hAndE[ind].second); + tmpLC.addHitAndEnergy(hAndF[ind].first, hAndE[ind].second); + tmpLC.addHitFraction(hAndF[ind].second); tmpLC.addHitTime(hAndT[ind].second); tmpLC.addHitPosition(hAndP[ind].second); }; diff --git a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc index fb463931821f3..bcffd0693684f 100644 --- a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc +++ b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc @@ -131,9 +131,9 @@ void PreMixingCaloParticleWorker::put(edm::Event &iEvent, std::vector const &ps, int bunchSpacing) { for (auto &sc : *newClusters_) { - auto hitsAndEnergies = sc.hits_and_fractions(); - sc.clearHitsAndFractions(); - sc.clearHitsEnergy(); + auto hitsAndEnergies = sc.hits_and_energies(); + sc.clearHitsAndEnergies(); + sc.clearFractions(); for (auto &hAndE : hitsAndEnergies) { const float totalenergy = totalEnergy_[hAndE.first]; float fraction = 0.; @@ -142,8 +142,8 @@ void PreMixingCaloParticleWorker::put(edm::Event &iEvent, else edm::LogWarning("PreMixingParticleWorker") << "TotalSimEnergy for hit " << hAndE.first << " is 0! The fraction for this hit cannot be computed."; - sc.addRecHitAndFraction(hAndE.first, fraction); - sc.addHitEnergy(hAndE.second); + sc.addRecHitAndEnergy(hAndE.first, hAndE.second); + sc.addHitFraction(fraction); } sc.finalizeHits(); } From c0d675d35f5f954e4eff4b4a0c08514e6fd1c377 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Wed, 1 Apr 2026 10:46:41 +0200 Subject: [PATCH 55/64] Set/unset CaloTruthAccumulator for procModifier combinations. --- .../Common/python/HLTValidation_cff.py | 7 +++- .../MixingModule/python/digitizers_cfi.py | 34 ++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/HLTriggerOffline/Common/python/HLTValidation_cff.py b/HLTriggerOffline/Common/python/HLTValidation_cff.py index d94c36371baaa..67552372c71fc 100644 --- a/HLTriggerOffline/Common/python/HLTValidation_cff.py +++ b/HLTriggerOffline/Common/python/HLTValidation_cff.py @@ -33,7 +33,12 @@ recHitsBH = ("hltHGCalRecHit", "HGCHEBRecHits", "HLT"), hgcalMultiClusters = "None", electrons = "None", - photons = "None" + photons = "None", + caloParticles = cms.InputTag("mix", "MergedCaloTruth") +) +from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 +premix_stage2.toModify(hgcalHitCalibrationHLT, + caloParticles = cms.InputTag("mixData", "MergedCaloTruth") ) # HGCAL validation diff --git a/SimGeneral/MixingModule/python/digitizers_cfi.py b/SimGeneral/MixingModule/python/digitizers_cfi.py index 565af87434d3c..0bf7980c84718 100644 --- a/SimGeneral/MixingModule/python/digitizers_cfi.py +++ b/SimGeneral/MixingModule/python/digitizers_cfi.py @@ -44,12 +44,17 @@ fastSim.toModify(theDigitizers, # fastsim does not digitize pixel and strip hits pixel = None, - tracks = recoTrackAccumulator + tracks = recoTrackAccumulator, + calotruth = None ) + +def _rmCaloTruthIfPresent(pset): + if hasattr(pset, "calotruth"): + pset.calotruth = None + from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 -(fastSim & premix_stage2).toModify(theDigitizers, - tracks = None -) +(fastSim & premix_stage2).toModify(theDigitizers, tracks = None) +(fastSim & premix_stage2).toModify(theDigitizers, _rmCaloTruthIfPresent) from SimCalorimetry.HGCalSimProducers.hgcalDigitizer_cfi import hgceeDigitizer, hgchebackDigitizer, hgchefrontDigitizer, HGCAL_noise_fC, HGCAL_noise_heback, HFNose_noise_fC, HGCAL_chargeCollectionEfficiencies, HGCAL_ileakParam_toUse, HGCAL_cceParams_toUse, HGCAL_noises @@ -59,7 +64,6 @@ hgceeDigitizer = cms.PSet(hgceeDigitizer), hgchebackDigitizer = cms.PSet(hgchebackDigitizer), hgchefrontDigitizer = cms.PSet(hgchefrontDigitizer), - calotruth = cms.PSet(caloParticles), #HGCAL still needs calotruth for production mode ) from SimCalorimetry.HGCalSimProducers.hgcalDigitizer_cfi import hfnoseDigitizer @@ -71,7 +75,8 @@ from Configuration.Eras.Modifier_run3_common_cff import run3_common # fastsim does not model castor -(run3_common | fastSim).toModify( theDigitizers, castor = None ) +(run3_common | fastSim).toModify(theDigitizers, castor = None) +(run3_common | fastSim).toModify(theDigitizers, _rmCaloTruthIfPresent) from SimGeneral.MixingModule.ecalTimeDigitizer_cfi import ecalTimeDigitizer from Configuration.Eras.Modifier_phase2_timing_cff import phase2_timing @@ -81,7 +86,7 @@ from SimGeneral.MixingModule.ecalTimeDigitizer_cfi import ecalTimeDigitizer from Configuration.Eras.Modifier_run3_ecal_devel_cff import run3_ecal_devel run3_ecal_devel.toModify( theDigitizers, - ecalTime = ecalTimeDigitizer.clone() ) + ecalTime = ecalTimeDigitizer.clone() ) from SimFastTiming.Configuration.SimFastTiming_cff import mtdDigitizer from Configuration.Eras.Modifier_phase2_timing_layer_cff import phase2_timing_layer @@ -94,6 +99,8 @@ ecal = None, hcal = None, ) +premix_stage2.toModify(theDigitizers, _rmCaloTruthIfPresent) + (premix_stage2 & phase2_hgcal).toModify(theDigitizers, hgceeDigitizer = dict(premixStage1 = True), hgchebackDigitizer = dict(premixStage1 = True), @@ -112,18 +119,13 @@ ) from Configuration.Eras.Modifier_phase2_tracker_cff import phase2_tracker -(phase2_tracker | fastSim).toModify(theDigitizers, - strip = None) - +(phase2_tracker | fastSim).toModify(theDigitizers, strip = None) +(phase2_tracker | fastSim).toModify(theDigitizers, _rmCaloTruthIfPresent) + theDigitizersValid = cms.PSet(theDigitizers) theDigitizers.mergedtruth.select.signalOnlyTP = True -(fastSim & phase2_hgcal).toModify(theDigitizersValid, calotruth = None) -#No calo clustering for fastsim phase2 - -from Configuration.ProcessModifiers.run3_ecalclustering_cff import run3_ecalclustering -run3_ecalclustering.toModify( theDigitizersValid, - calotruth = cms.PSet( caloParticles ) ) +(fastSim & phase2_hgcal).toModify(theDigitizersValid, _rmCaloTruthIfPresent) phase2_timing.toModify( theDigitizersValid.mergedtruth, createInitialVertexCollection = cms.bool(True) ) From 4950f9e1cfd16ded617773f560d77b7578220fae Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 7 Apr 2026 19:34:51 +0200 Subject: [PATCH 56/64] Add asserts. --- .../CaloAnalysis/interface/SimCluster.h | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index 41b902c3decbe..5797e4c2fbbdc 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -279,6 +279,7 @@ class SimCluster { /** @brief Returns filtered list of rechit IDs and fractions for this SimCluster based on a predicate (copying) */ std::vector> filtered_hits_and_fractions( const std::function &predicate) const { + assert(hits_.size() == fractions_.size()); std::vector> result; for (size_t i = 0; i < hits_.size(); ++i) { DetId detid(hits_[i]); @@ -323,7 +324,9 @@ class SimCluster { // -------------------------------------------------------------------------- void finalizeHits() { // Keep your original implicit invariant: - assert(hits_.size() == fractions_.size() && hits_.size() == energies_.size() && !hits_.empty()); + assert(hits_.size() == energies_.size() && !hits_.empty()); + if (!fractions_.empty()) + assert(hits_.size() == fractions_.size()); // Already finalized? keep it cheap and idempotent. if (hitsFinalized_) @@ -341,8 +344,9 @@ class SimCluster { std::sort(order.begin(), order.end(), [&](size_t a, size_t b) { return key(a) < key(b); }); applyPermutation_(hits_, order); - applyPermutation_(fractions_, order); applyPermutation_(energies_, order); + if (!fractions_.empty()) + applyPermutation_(fractions_, order); buildDetRanges_(); @@ -354,23 +358,27 @@ class SimCluster { // -------------------------------------------------------------------------- HitsAndFractionsView hits_and_fractions_view() const { assertFinalized_(); + assertFractions_(); return HitsAndFractionsView{{hits_, fractions_}}; } HitsAndFractionsView hits_and_fractions_view(DetId::Detector det) const { assertFinalized_(); + assertFractions_(); auto [b, e] = detRanges_[detIndex_(det)]; return HitsAndFractionsView{{{hits_.data() + b, e - b}, {fractions_.data() + b, e - b}}}; } HitsAndFractionsView hits_and_fractions_view(DetId::Detector det, int subdetId) const { assertFinalized_(); + assertFractions_(); auto [bb, ee] = subdetRange_(det, subdetId); return HitsAndFractionsView{{{hits_.data() + bb, ee - bb}, {fractions_.data() + bb, ee - bb}}}; } HitsAndFractionsView hits_and_fractions_view(DetId::Detector detIdMin, DetId::Detector detIdMax) const { assertFinalized_(); + assertFractions_(); auto [begin, end] = detMinMaxRange_(detIdMin, detIdMax); return HitsAndFractionsView{{{hits_.data() + begin, end - begin}, {fractions_.data() + begin, end - begin}}}; } @@ -431,6 +439,15 @@ class SimCluster { assert(hitsFinalized_ && "SimCluster: hits not finalized. Call finalizeHits() in the producer before persisting."); } + void assertFractions_() const { + if (hits_.size() != fractions_.size()) { + std::cerr << "SimCluster: fractions (" + std::to_string(fractions_.size()) + ") and hits (" + + std::to_string(hits_.size()) + ") must have the same size." + << std::endl; + std::abort(); + } + } + // Returns the [begin, end) index range within hits_ for a det+subdet pair. std::pair subdetRange_(DetId::Detector det, int subdetId) const { auto [b, e] = detRanges_[detIndex_(det)]; From 49aaf254df8846d91882784125401d53b6bfe243 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 7 Apr 2026 19:35:26 +0200 Subject: [PATCH 57/64] Support premix workflow (mix -> mixData). --- .../plugins/CaloTruthAccumulator.cc | 1 - .../plugins/PreMixingCaloParticleWorker.cc | 4 ++-- .../Configuration/python/hgcalSimValid_cff.py | 9 +++++++- .../python/hltHGCalSimValid_cff.py | 5 +++++ .../python/HLTHGCalValidator_cff.py | 19 ++++++++++++++-- .../python/hltPFValidation_cfi.py | 22 +++++++++++++++---- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index 28329fa196ee7..bced1b22fd900 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -676,7 +676,6 @@ void CaloTruthAccumulator::initializeEvent(edm::Event const &event, edm::EventSe void CaloTruthAccumulator::accumulate(edm::Event const &event, edm::EventSetup const &setup) { edm::Handle hepmc; event.getByLabel(hepMCproductLabel_, hepmc); - edm::LogInfo(messageCategory_) << " CaloTruthAccumulator::accumulate (signal)"; accumulateEvent(event, setup, hepmc); } diff --git a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc index bcffd0693684f..9ac6b9ca48ede 100644 --- a/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc +++ b/SimGeneral/CaloAnalysis/plugins/PreMixingCaloParticleWorker.cc @@ -131,10 +131,10 @@ void PreMixingCaloParticleWorker::put(edm::Event &iEvent, std::vector const &ps, int bunchSpacing) { for (auto &sc : *newClusters_) { - auto hitsAndEnergies = sc.hits_and_energies(); + auto hitsAndEnergies = sc.hits_and_energies_view(); sc.clearHitsAndEnergies(); sc.clearFractions(); - for (auto &hAndE : hitsAndEnergies) { + for (auto hAndE : hitsAndEnergies) { const float totalenergy = totalEnergy_[hAndE.first]; float fraction = 0.; if (totalenergy > 0) diff --git a/Validation/Configuration/python/hgcalSimValid_cff.py b/Validation/Configuration/python/hgcalSimValid_cff.py index da30e4e635af7..d20ded39ff264 100644 --- a/Validation/Configuration/python/hgcalSimValid_cff.py +++ b/Validation/Configuration/python/hgcalSimValid_cff.py @@ -15,7 +15,7 @@ from SimCalorimetry.HGCalAssociatorProducers.HitToTracksterAssociation_cfi import allHitToTracksterAssociations from SimCalorimetry.HGCalAssociatorProducers.TSToSimTSAssociation_cfi import allTrackstersToSimTrackstersAssociationsByLCs from SimCalorimetry.HGCalAssociatorProducers.TSToSimTSAssociationByHits_cfi import allTrackstersToSimTrackstersAssociationsByHits -from SimCalorimetry.HGCalAssociatorProducers.hitToSimClusterCaloParticleAssociator_cfi import hitToSimClusterCaloParticleAssociator +from SimCalorimetry.HGCalAssociatorProducers.hitToSimClusterCaloParticleAssociator_cfi import hitToSimClusterCaloParticleAssociator as _hitToSimClusterCaloParticleAssociator from SimCalorimetry.HGCalAssociatorProducers.SimClusterToCaloParticleAssociation_cfi import SimClusterToCaloParticleAssociation @@ -42,6 +42,13 @@ VariablePtBins=[10., 30., 80., 120., 250., 600.], DeltaPtOvPtHistoParameter = dict(EROn=True,EREtaMax=3.0, EREtaMin=1.6, slicingOn=True)) +hitToSimClusterCaloParticleAssociator = _hitToSimClusterCaloParticleAssociator.clone() +from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 +premix_stage2.toModify(hitToSimClusterCaloParticleAssociator, + caloParticles = cms.InputTag("mixData", "MergedCaloTruth"), + simClusters = cms.InputTag("mixData", "MergedCaloTruth") +) + hgcalAssociators = cms.Task(lcAssocByEnergyScoreProducer, layerClusterCaloParticleAssociationProducer, scAssocByEnergyScoreProducer, layerClusterSimClusterAssociationProducer, SimTauProducer, diff --git a/Validation/Configuration/python/hltHGCalSimValid_cff.py b/Validation/Configuration/python/hltHGCalSimValid_cff.py index df93c7522f243..12fc4722bf739 100644 --- a/Validation/Configuration/python/hltHGCalSimValid_cff.py +++ b/Validation/Configuration/python/hltHGCalSimValid_cff.py @@ -130,6 +130,11 @@ hitMap = 'hltHGCalRecHitMapProducer:hgcalRecHitMap', hits = 'hltHGCalRecHitMapProducer:RefProdVectorHGCRecHitCollection' ) +from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 +premix_stage2.toModify(hltHitToSimClusterCaloParticleAssociator, + caloParticles = cms.InputTag("mixData", "MergedCaloTruth"), + simClusters = cms.InputTag("mixData", "MergedCaloTruth") +) from SimCalorimetry.HGCalAssociatorProducers.AllHitToTracksterAssociatorsProducer_cfi import AllHitToTracksterAssociatorsProducer as _AllHitToTracksterAssociatorsProducer diff --git a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py index 018fb0a030c0a..d1abf88351814 100644 --- a/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py +++ b/Validation/HGCalValidation/python/HLTHGCalValidator_cff.py @@ -32,6 +32,9 @@ ticlTrackstersMerge = 'hltTiclTrackstersMerge', mergeRecoToSimAssociator = 'hltAllTrackstersToSimTrackstersAssociationsByLCs:hltTiclTrackstersMergeTohltTiclSimTrackstersfromCPs', mergeSimToRecoAssociator = 'hltAllTrackstersToSimTrackstersAssociationsByLCs:hltTiclSimTrackstersfromCPsTohltTiclTrackstersMerge', + label_cp_effic = cms.InputTag("mix", "MergedCaloTruth"), + label_cp_fake = cms.InputTag("mix", "MergedCaloTruth"), + label_scl = cms.InputTag("mix", "MergedCaloTruth") ) from Configuration.ProcessModifiers.ticl_v5_cff import ticl_v5 @@ -47,12 +50,19 @@ mergeRecoToSimAssociator = cms.InputTag("hltAllTrackstersToSimTrackstersAssociationsByLCs:hltTiclCandidateTohltTiclSimTrackstersfromCPs"), ) +from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 +premix_stage2.toModify(hltHgcalValidator, + label_cp_effic = cms.InputTag("mixData", "MergedCaloTruth"), + label_cp_fake = cms.InputTag("mixData", "MergedCaloTruth"), + label_scl = cms.InputTag("mixData", "MergedCaloTruth") +) + hltLayerClusterTesterECAL = cms.EDProducer("CaloClusterTester", PFCand = cms.InputTag("hltParticleFlowTmp"), Rechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), RecoCluster = cms.InputTag("hltBarrelLayerClustersEB"), - SimCluster = cms.InputTag("mix","MergedCaloTruth"), - CaloParticle = cms.InputTag("mix","MergedCaloTruth"), + SimCluster = cms.InputTag("mix", "MergedCaloTruth"), + CaloParticle = cms.InputTag("mix", "MergedCaloTruth"), ClusterSimClusterAssociator = cms.InputTag("hltBarrelLayerClusterSimClusterAssociationProducer"), ClusterCaloParticleAssociator = cms.InputTag("hltBarrelLayerClusterCaloParticleAssociationProducer"), outFolder = cms.string('HLT/TiclBarrel'), @@ -62,6 +72,11 @@ ptCut = cms.double(0.), etaCut = cms.double(1.48) ) +from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 +premix_stage2.toModify(hltLayerClusterTesterECAL, + SimCluster = cms.InputTag("mixData", "MergedCaloTruth"), + CaloParticle = cms.InputTag("mixData", "MergedCaloTruth"), +) hltLayerClusterTesterECALWithCut = hltLayerClusterTesterECAL.clone( enFracCut = cms.double(0.01), diff --git a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py index 54c58074747e8..564ec1a99ea52 100644 --- a/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py +++ b/Validation/RecoParticleFlow/python/hltPFValidation_cfi.py @@ -2,6 +2,9 @@ _filter_sim_hits = cms.vstring("Ecal",) +_calo_truth = cms.InputTag("mix", "MergedCaloTruth") +_calo_truth_premix = cms.InputTag("mixData", "MergedCaloTruth") + hltPFScAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToSCAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), hitMapTag = cms.InputTag("hltRecHitMapProducer:pfRecHitMap"), @@ -11,9 +14,13 @@ hltPFClusterSimClusterAssociationProducerECAL = cms.EDProducer("PCToSCAssociatorEDProducer", associator = cms.InputTag("hltPFScAssocByEnergyScoreProducer"), label_lcl = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - label_scl = cms.InputTag("mix", "MergedCaloTruth"), + label_scl = _calo_truth, filter_sim_hits = _filter_sim_hits ) +from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 +premix_stage2.toModify(hltPFClusterSimClusterAssociationProducerECAL, + label_scl = _calo_truth_premix, +) hltPFCpAssocByEnergyScoreProducer = cms.EDProducer("BarrelPCToCPAssociatorByEnergyScoreProducer", hardScatterOnly = cms.bool(True), @@ -24,16 +31,19 @@ hltPFClusterCaloParticleAssociationProducerECAL = cms.EDProducer("PCToCPAssociatorEDProducer", associator = cms.InputTag("hltPFCpAssocByEnergyScoreProducer"), label_lc = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - label_cp = cms.InputTag("mix", "MergedCaloTruth"), + label_cp = _calo_truth, filter_sim_hits = _filter_sim_hits ) +premix_stage2.toModify(hltPFClusterCaloParticleAssociationProducerECAL, + label_cp = _calo_truth_premix, +) hltPFClusterTesterECAL = cms.EDProducer("PFClusterTester", PFCand = cms.InputTag("hltParticleFlow"), Rechit = cms.InputTag("hltParticleFlowRecHitECALUnseeded"), RecoCluster = cms.InputTag("hltParticleFlowClusterECALUnseeded"), - SimCluster = cms.InputTag("mix", "MergedCaloTruth"), - CaloParticle = cms.InputTag("mix", "MergedCaloTruth"), + SimCluster = _calo_truth, + CaloParticle = _calo_truth, ClusterSimClusterAssociator = cms.InputTag("hltPFClusterSimClusterAssociationProducerECAL"), ClusterCaloParticleAssociator = cms.InputTag("hltPFClusterCaloParticleAssociationProducerECAL"), filter_sim_hits = _filter_sim_hits, @@ -46,6 +56,10 @@ ) from Configuration.Eras.Modifier_phase2_common_cff import phase2_common phase2_common.toModify(hltPFClusterTesterECAL, PFCand = cms.InputTag("hltParticleFlowTmp"), etaCut = cms.double(1.48)) +premix_stage2.toModify(hltPFClusterTesterECAL, + SimCluster = _calo_truth_premix, + CaloParticle = _calo_truth_premix, +) hltDigisTesterECAL = cms.EDProducer("DigisTester", ecalEBDigis = cms.InputTag("hltEcalDigis", "ebDigis"), From 45986c34b0c848d6a2a1e088e3a81fb91bbf27bc Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 7 Apr 2026 20:04:17 +0200 Subject: [PATCH 58/64] Add dictionaries to reduce memory increase. --- DataFormats/ParticleFlowReco/src/classes_def_2.xml | 2 +- SimDataFormats/Associations/src/classes_def.xml | 14 ++++++++++++-- SimDataFormats/CaloAnalysis/src/classes_def.xml | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/DataFormats/ParticleFlowReco/src/classes_def_2.xml b/DataFormats/ParticleFlowReco/src/classes_def_2.xml index 9b21aebde986b..2f0f2670b6b91 100644 --- a/DataFormats/ParticleFlowReco/src/classes_def_2.xml +++ b/DataFormats/ParticleFlowReco/src/classes_def_2.xml @@ -223,9 +223,9 @@ + - diff --git a/SimDataFormats/Associations/src/classes_def.xml b/SimDataFormats/Associations/src/classes_def.xml index f61f51cbd2a2b..355db8f01bd4e 100644 --- a/SimDataFormats/Associations/src/classes_def.xml +++ b/SimDataFormats/Associations/src/classes_def.xml @@ -174,6 +174,11 @@ + + - + + + - + diff --git a/SimDataFormats/CaloAnalysis/src/classes_def.xml b/SimDataFormats/CaloAnalysis/src/classes_def.xml index e828ff3b679d2..aae92e6f46a18 100644 --- a/SimDataFormats/CaloAnalysis/src/classes_def.xml +++ b/SimDataFormats/CaloAnalysis/src/classes_def.xml @@ -36,7 +36,8 @@ - + + From 1c25b8a5f1e9013d3791970c4d316c024accc661 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 14 Apr 2026 15:47:56 +0200 Subject: [PATCH 59/64] Resolve compilation due to conflicts. --- .../CaloAnalysis/interface/CaloParticle.h | 33 +++++++++++++++---- .../CaloAnalysis/src/classes_def.xml | 8 +++-- .../plugins/CaloTruthAccumulator.cc | 26 ++++++++++----- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/CaloParticle.h b/SimDataFormats/CaloAnalysis/interface/CaloParticle.h index a942249325ea2..0469a305b41fb 100644 --- a/SimDataFormats/CaloAnalysis/interface/CaloParticle.h +++ b/SimDataFormats/CaloAnalysis/interface/CaloParticle.h @@ -166,14 +166,18 @@ class CaloParticle { /** @brief returns the time in ns of the caloparticle */ float simTime() const { return time_; } - /** @brief add rechit with fraction */ - void addRecHitAndFraction(uint32_t hit, float fraction) { + /** @brief add rechit with energy */ + void addRecHitAndEnergy(uint32_t hit, float energy) { hits_.emplace_back(hit); - fractions_.emplace_back(fraction); + energies_.emplace_back(energy); } + /** @brief add rechit fraction */ + void addHitFraction(float fraction) { fractions_.emplace_back(fraction); } + /** @brief Returns list of rechit IDs and fractions for this CaloParticle */ std::vector> hits_and_fractions() const { + assert(hits_.size() == fractions_.size()); std::vector> result; result.reserve(hits_.size()); for (size_t i = 0; i < hits_.size(); ++i) { @@ -182,12 +186,26 @@ class CaloParticle { return result; } - /** @brief clear the hits and fractions list */ - void clearHitsAndFractions() { - hits_.clear(); - fractions_.clear(); + /** @brief Returns list of rechit IDs and energies for this CaloParticle */ + std::vector> hits_and_energies() const { + assert(hits_.size() == energies_.size()); + std::vector> result; + result.reserve(hits_.size()); + for (size_t i = 0; i < hits_.size(); ++i) { + result.emplace_back(hits_[i], energies_[i]); + } + return result; + } + + /** @brief clear the hits and energies list */ + void clearHitsAndEnergies() { + std::vector().swap(hits_); + std::vector().swap(energies_); } + /** @brief clear the hits and fractions list */ + void clearFractions() { std::vector().swap(fractions_); } + /** @brief returns the accumulated sim energy in the cluster */ float simEnergy() const { return simhit_energy_; } @@ -209,6 +227,7 @@ class CaloParticle { float time_{std::numeric_limits::lowest()}; std::vector hits_; std::vector fractions_; + std::vector energies_; math::XYZTLorentzVectorF theMomentum_; diff --git a/SimDataFormats/CaloAnalysis/src/classes_def.xml b/SimDataFormats/CaloAnalysis/src/classes_def.xml index aae92e6f46a18..0bbc64f0d8d82 100644 --- a/SimDataFormats/CaloAnalysis/src/classes_def.xml +++ b/SimDataFormats/CaloAnalysis/src/classes_def.xml @@ -1,7 +1,8 @@ - + + - + @@ -11,7 +12,8 @@ - + + diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index bced1b22fd900..2c46b6e2a2411 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include "FWCore/Framework/interface/ConsumesCollector.h" #include "FWCore/Framework/interface/ESWatcher.h" @@ -696,7 +697,7 @@ void CaloTruthAccumulator::accumulate(PileUpEventPrincipal const &event, } namespace { - /** Normalize the energies in the SimCluster/CaloParticle collection, from absolute SimHit energies to fraction of total simHits energies */ + /** Normalize the energies in the SimCluster/CaloParticle collection, from absolute SimHit energies to fraction of total simHits energies */ template void normalizeCollection(SimCaloCollection &simClusters, std::unordered_map const &detIdToTotalSimEnergy) { @@ -713,7 +714,14 @@ namespace { << "TotalSimEnergy for hit " << hAndE.first << " is 0! The fraction for this hit cannot be computed."; sc.addHitFraction(fraction); } - sc.finalizeHits(); + } + } + + /** "Finalize" the collection production; to be called before putting it in the event */ + template + void finalizeCollection(SimCaloCollection &simClusters) { + for (auto &sc : simClusters) { + sc.finalizeHits(); } } }; // namespace @@ -727,19 +735,21 @@ void CaloTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup cons // we have looped over all pileup events) // For premixing stage1 we keep the energies, they will be normalized to // fractions in stage2 (in PreMixingCaloParticleWorker.cc) - if (premixStage1_) { auto totalEnergies = std::make_unique>>(); totalEnergies->reserve(m_detIdToTotalSimEnergy.size()); std::copy(m_detIdToTotalSimEnergy.begin(), m_detIdToTotalSimEnergy.end(), std::back_inserter(*totalEnergies)); std::sort(totalEnergies->begin(), totalEnergies->end()); event.put(std::move(totalEnergies), "MergedCaloTruth"); - for (auto &sc : *(output_.pSimClusters)) { - sc.finalizeHits(); // make sure persisted SimClusters have sorted hits and built ranges - } + + // make sure persisted SimClusters have sorted hits and built ranges + applyToSimClusterConfig([this](auto &config) { finalizeCollection(config.outputClusters); }); + } else { - applyToSimClusterConfig( - [this](auto &config) { normalizeCollection(config.outputClusters, m_detIdToTotalSimEnergy); }); + applyToSimClusterConfig([this](auto &config) { + normalizeCollection(config.outputClusters, m_detIdToTotalSimEnergy); + finalizeCollection(config.outputClusters); + }); normalizeCollection(outputCaloParticles_, m_detIdToTotalSimEnergy); } From 66704facaa68fb3a8ad99032a50f4469fdb0f85c Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 14 Apr 2026 20:37:19 +0200 Subject: [PATCH 60/64] Address premixing assertion errors and merge simplification. --- .../CaloAnalysis/interface/CaloParticle.h | 4 ++ .../CaloAnalysis/interface/SimCluster.h | 50 ++++--------------- SimDataFormats/CaloAnalysis/src/SimCluster.cc | 41 +++++++++++++++ .../plugins/CaloTruthAccumulator.cc | 24 +++++---- 4 files changed, 68 insertions(+), 51 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/CaloParticle.h b/SimDataFormats/CaloAnalysis/interface/CaloParticle.h index 0469a305b41fb..2b1915e9695f3 100644 --- a/SimDataFormats/CaloAnalysis/interface/CaloParticle.h +++ b/SimDataFormats/CaloAnalysis/interface/CaloParticle.h @@ -197,6 +197,10 @@ class CaloParticle { return result; } + /** @brief Returns view to list of rechit IDs and energies for this CaloParticle + TODO: Implement views for CaloParticles as done for SimClusters. */ + std::vector> hits_and_energies_view() const { return hits_and_energies(); } + /** @brief clear the hits and energies list */ void clearHitsAndEnergies() { std::vector().swap(hits_); diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index 5797e4c2fbbdc..6e5a2139f058e 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -17,6 +17,7 @@ #include #include +#include "FWCore/MessageLogger/interface/MessageLogger.h" #include "DataFormats/DetId/interface/DetId.h" #include "DataFormats/HepMCCandidate/interface/GenParticle.h" #include "DataFormats/Math/interface/LorentzVector.h" @@ -125,9 +126,7 @@ class SimCluster { ~SimCluster() = default; /** Build a SimCluster from a collection of SimCluster. Hits&fractions are merged, SimTracks are all added in g4Tracks_ */ - template - requires std::ranges::input_range && std::same_as, SimCluster> - static SimCluster mergeHitsFromCollection(R const &); + static SimCluster mergeHitsFromCollection(const std::vector &inputs); /** @brief PDG ID. * @@ -323,8 +322,12 @@ class SimCluster { // Producer-side "finalization" (to be called before putting in the event) // -------------------------------------------------------------------------- void finalizeHits() { - // Keep your original implicit invariant: - assert(hits_.size() == energies_.size() && !hits_.empty()); + if (hits_.empty()) { + hitsFinalized_ = true; + return; + } + + assert(hits_.size() == energies_.size()); if (!fractions_.empty()) assert(hits_.size() == fractions_.size()); @@ -441,9 +444,8 @@ class SimCluster { void assertFractions_() const { if (hits_.size() != fractions_.size()) { - std::cerr << "SimCluster: fractions (" + std::to_string(fractions_.size()) + ") and hits (" + - std::to_string(hits_.size()) + ") must have the same size." - << std::endl; + edm::LogWarning("SimCluster") << "fractions (" + std::to_string(fractions_.size()) + ") and hits (" + + std::to_string(hits_.size()) + ") must have the same size."; std::abort(); } } @@ -507,36 +509,4 @@ class SimCluster { } }; -template - requires std::ranges::input_range && std::same_as, SimCluster> -SimCluster SimCluster::mergeHitsFromCollection(R const &inputs) { - assert(!std::ranges::empty(inputs)); - SimCluster ret; - ret.event_ = inputs[0].event_; - ret.particleId_ = inputs[0].particleId_; - - ret.g4Tracks_.reserve(inputs.size()); - std::unordered_map acc_fractions; ///< Map DetId->(fraction) - for (SimCluster const &other : inputs) { - ret.simhit_energy_ += other.simhit_energy_; - - assert(other.hits_.size() == other.fractions_.size()); - for (std::size_t i = 0; i < other.hits_.size(); i++) { - acc_fractions[other.hits_[i]] += other.fractions_[i]; - } - - ret.g4Tracks_.insert(ret.g4Tracks_.end(), other.g4Tracks_.begin(), other.g4Tracks_.end()); - } - - ret.hits_.reserve(acc_fractions.size()); - ret.fractions_.reserve(acc_fractions.size()); - for (const auto &hit_and_fraction : acc_fractions) { - ret.hits_.push_back(hit_and_fraction.first); - ret.fractions_.push_back(hit_and_fraction.second); - } - ret.nsimhits_ = ret.hits_.size(); - - return ret; -} - #endif // SimDataFormats_SimCluster_H diff --git a/SimDataFormats/CaloAnalysis/src/SimCluster.cc b/SimDataFormats/CaloAnalysis/src/SimCluster.cc index 799ba102700f7..f02996a04f474 100644 --- a/SimDataFormats/CaloAnalysis/src/SimCluster.cc +++ b/SimDataFormats/CaloAnalysis/src/SimCluster.cc @@ -21,6 +21,47 @@ SimCluster::SimCluster(EncodedEventId eventID, uint32_t particleID) { particleId_ = particleID; } +SimCluster SimCluster::mergeHitsFromCollection(const std::vector &inputs) { + assert(!inputs.empty()); + SimCluster ret; + ret.event_ = inputs[0].event_; + ret.particleId_ = inputs[0].particleId_; + + ret.g4Tracks_.reserve(inputs.size()); + std::unordered_map acc_energies, acc_fractions; + for (SimCluster const &other : inputs) { + ret.simhit_energy_ += other.simhit_energy_; + + assert(other.hits_.size() == other.energies_.size()); + for (std::size_t i = 0; i < other.hits_.size(); i++) { + acc_energies[other.hits_[i]] += other.energies_[i]; + } + + if (!other.fractions_.empty()) { + assert(other.hits_.size() == other.fractions_.size()); + for (std::size_t i = 0; i < other.hits_.size(); i++) { + acc_fractions[other.hits_[i]] += other.fractions_[i]; + } + } + + ret.g4Tracks_.insert(ret.g4Tracks_.end(), other.g4Tracks_.begin(), other.g4Tracks_.end()); + } + + ret.hits_.reserve(acc_energies.size()); + ret.energies_.reserve(acc_energies.size()); + ret.fractions_.reserve(acc_fractions.size()); + for (auto const &[detId, energy] : acc_energies) { + ret.hits_.push_back(detId); + ret.energies_.push_back(energy); + } + for (auto const &[_, fraction] : acc_fractions) { + ret.fractions_.push_back(fraction); + } + ret.nsimhits_ = ret.hits_.size(); + + return ret; +} + std::ostream &operator<<(std::ostream &s, SimCluster const &tp) { s << "CP momentum, q, ID, & Event #: " << tp.p4() << " " << tp.charge() << " " << tp.pdgId() << " " << tp.eventId().bunchCrossing() << "." << tp.eventId().event() << std::endl; diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index 2c46b6e2a2411..1a8ce8037d6a1 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include "FWCore/Framework/interface/ConsumesCollector.h" #include "FWCore/Framework/interface/ESWatcher.h" @@ -148,10 +149,11 @@ namespace { mapToSub[pseudoJet.user_index()] = clusters_.size(); } - clusters_.emplace_back(SimCluster::mergeHitsFromCollection( - constituents | std::views::transform([&](fastjet::PseudoJet const &pseudoJet) { - return subClustersBuilt[static_cast(pseudoJet.user_index())]; - }))); + std::vector tmpSimClusters; + tmpSimClusters.reserve(constituents.size()); + for (auto const &pj : constituents) + tmpSimClusters.push_back(subClustersBuilt[static_cast(pj.user_index())]); + clusters_.emplace_back(SimCluster::mergeHitsFromCollection(tmpSimClusters)); // Record CaloParticle index for the merged SimCluster caloParticleParentIndexRecorder_.recordParentClusterIndex(currentCaloParticleIndex); @@ -320,7 +322,7 @@ namespace { // to check if SimTrack has simHits : use DecayGraph EdgeProperty /** Given a track G4 ID, give map DetId->accumulated SimHit energy */ - std::map const &hits_and_energies(unsigned int trackIdx) const { + std::map const &hits_and_energies_map(unsigned int trackIdx) const { return simTrackDetIdEnergyMap_.at(trackIdx); } @@ -350,15 +352,15 @@ namespace { return; // Should not happen auto trackIdx = edge_simTrack->trackId(); if (edge_property.simHits != 0) { - for (auto const &hit_and_energy : helper_.hits_and_energies(trackIdx)) { - acc_energy[hit_and_energy.first] += hit_and_energy.second; + for (auto const &[detId, energy] : helper_.hits_and_energies_map(trackIdx)) { + acc_energy[detId] += energy; } } } void doEndCluster(SimClassNameT &cluster) { - for (auto const &hit_and_energy : acc_energy) { - cluster.addRecHitAndEnergy(hit_and_energy.first, hit_and_energy.second); + for (auto const &[detId, energy] : acc_energy) { + cluster.addRecHitAndEnergy(detId, energy); } acc_energy.clear(); } @@ -702,9 +704,9 @@ namespace { void normalizeCollection(SimCaloCollection &simClusters, std::unordered_map const &detIdToTotalSimEnergy) { for (auto &sc : simClusters) { - auto hitsAndEnergies = sc.hits_and_fractions(); + auto hitsAndEnergies = sc.hits_and_energies_view(); sc.clearFractions(); - for (auto &hAndE : hitsAndEnergies) { + for (auto hAndE : hitsAndEnergies) { const float totalenergy = detIdToTotalSimEnergy.at(hAndE.first); float fraction = 0.; if (totalenergy > 0) From 092e638a87a2ee65c4254750f1faeec7f0ad64d3 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Wed, 15 Apr 2026 15:06:50 +0200 Subject: [PATCH 61/64] Remove capture warning. --- SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc index 1a8ce8037d6a1..c19c1b9febd7d 100644 --- a/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc +++ b/SimGeneral/CaloAnalysis/plugins/CaloTruthAccumulator.cc @@ -21,7 +21,6 @@ #include #include #include -#include #include "FWCore/Framework/interface/ConsumesCollector.h" #include "FWCore/Framework/interface/ESWatcher.h" @@ -745,11 +744,11 @@ void CaloTruthAccumulator::finalizeEvent(edm::Event &event, edm::EventSetup cons event.put(std::move(totalEnergies), "MergedCaloTruth"); // make sure persisted SimClusters have sorted hits and built ranges - applyToSimClusterConfig([this](auto &config) { finalizeCollection(config.outputClusters); }); + applyToSimClusterConfig([](auto &config) { finalizeCollection(config.outputClusters); }); } else { applyToSimClusterConfig([this](auto &config) { - normalizeCollection(config.outputClusters, m_detIdToTotalSimEnergy); + normalizeCollection(config.outputClusters, this->m_detIdToTotalSimEnergy); finalizeCollection(config.outputClusters); }); normalizeCollection(outputCaloParticles_, m_detIdToTotalSimEnergy); From 28012a955195a5f2a227e2eb1ca9c61ed44c18a2 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 16 Apr 2026 17:19:41 +0200 Subject: [PATCH 62/64] Full view ordering not required + sanity checks. --- .../CaloAnalysis/interface/SimCluster.h | 37 +++++++++++++------ SimDataFormats/CaloAnalysis/src/SimCluster.cc | 12 ++++-- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index 6e5a2139f058e..efbecd7a327a8 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -133,9 +133,10 @@ class SimCluster { * Returns the PDG ID of the first associated gen particle. If there are no * gen particles associated then it returns type() from the first SimTrack. */ int pdgId() const { - if (genParticles_.empty()) + if (genParticles_.empty()) { + assert(!g4Tracks_.empty()); return g4Tracks_[0].type(); - else + } else return (*genParticles_.begin())->pdgId(); } @@ -162,7 +163,10 @@ class SimCluster { const std::vector &g4Tracks() const { return g4Tracks_; } /// @brief Electric charge. Note this is taken from the first SimTrack only. - float charge() const { return g4Tracks_[0].charge(); } + float charge() const { + assert(!g4Tracks_.empty()); + return g4Tracks_[0].charge(); + } /// Gives charge in unit of quark charge (should be 3 times "charge()") int threeCharge() const { return lrintf(3.f * charge()); } @@ -267,6 +271,7 @@ class SimCluster { /** @brief Returns list of rechit IDs and fractions for this SimCluster (copying legacy API) */ std::vector> hits_and_fractions() const { // legacy returns a copy; now deterministic because finalizeHits() sorted it + assertFractions_(); std::vector> result; result.reserve(hits_.size()); for (size_t i = 0; i < hits_.size(); ++i) { @@ -278,7 +283,7 @@ class SimCluster { /** @brief Returns filtered list of rechit IDs and fractions for this SimCluster based on a predicate (copying) */ std::vector> filtered_hits_and_fractions( const std::function &predicate) const { - assert(hits_.size() == fractions_.size()); + assertFractions_(); std::vector> result; for (size_t i = 0; i < hits_.size(); ++i) { DetId detid(hits_[i]); @@ -290,7 +295,7 @@ class SimCluster { /** @brief Returns list of rechit IDs and energies for this SimCluster (copying legacy API) */ std::vector> hits_and_energies() const { - assert(hits_.size() == energies_.size()); + assertEnergies_(); std::vector> result; result.reserve(hits_.size()); for (size_t i = 0; i < hits_.size(); ++i) @@ -327,14 +332,14 @@ class SimCluster { return; } - assert(hits_.size() == energies_.size()); - if (!fractions_.empty()) - assert(hits_.size() == fractions_.size()); - // Already finalized? keep it cheap and idempotent. if (hitsFinalized_) return; + assertEnergies_(); + if (!fractions_.empty()) + assertFractions_(); + // Sort by (det, subdet, rawid) std::vector order(hits_.size()); std::iota(order.begin(), order.end(), 0); @@ -360,7 +365,6 @@ class SimCluster { // cost-free views (requires a call to finalizeHits()) // -------------------------------------------------------------------------- HitsAndFractionsView hits_and_fractions_view() const { - assertFinalized_(); assertFractions_(); return HitsAndFractionsView{{hits_, fractions_}}; } @@ -387,24 +391,27 @@ class SimCluster { } HitsAndEnergiesView hits_and_energies_view() const { - assertFinalized_(); + assertEnergies_(); return HitsAndEnergiesView{{hits_, energies_}}; } HitsAndEnergiesView hits_and_energies_view(DetId::Detector det) const { assertFinalized_(); + assertEnergies_(); auto [b, e] = detRanges_[detIndex_(det)]; return HitsAndEnergiesView{{{hits_.data() + b, e - b}, {energies_.data() + b, e - b}}}; } HitsAndEnergiesView hits_and_energies_view(DetId::Detector det, int subdetId) const { assertFinalized_(); + assertEnergies_(); auto [bb, ee] = subdetRange_(det, subdetId); return HitsAndEnergiesView{{{hits_.data() + bb, ee - bb}, {energies_.data() + bb, ee - bb}}}; } HitsAndEnergiesView hits_and_energies_view(DetId::Detector detIdMin, DetId::Detector detIdMax) const { assertFinalized_(); + assertEnergies_(); auto [begin, end] = detMinMaxRange_(detIdMin, detIdMax); return HitsAndEnergiesView{{{hits_.data() + begin, end - begin}, {energies_.data() + begin, end - begin}}}; } @@ -442,6 +449,14 @@ class SimCluster { assert(hitsFinalized_ && "SimCluster: hits not finalized. Call finalizeHits() in the producer before persisting."); } + void assertEnergies_() const { + if (hits_.size() != energies_.size()) { + edm::LogWarning("SimCluster") << "energies (" + std::to_string(energies_.size()) + ") and hits (" + + std::to_string(hits_.size()) + ") must have the same size."; + std::abort(); + } + } + void assertFractions_() const { if (hits_.size() != fractions_.size()) { edm::LogWarning("SimCluster") << "fractions (" + std::to_string(fractions_.size()) + ") and hits (" + diff --git a/SimDataFormats/CaloAnalysis/src/SimCluster.cc b/SimDataFormats/CaloAnalysis/src/SimCluster.cc index f02996a04f474..c2eba74564bf5 100644 --- a/SimDataFormats/CaloAnalysis/src/SimCluster.cc +++ b/SimDataFormats/CaloAnalysis/src/SimCluster.cc @@ -44,6 +44,7 @@ SimCluster SimCluster::mergeHitsFromCollection(const std::vector &in } } + assert(!other.g4Tracks_.empty()); // otherwise we could end up without any sim track ret.g4Tracks_.insert(ret.g4Tracks_.end(), other.g4Tracks_.begin(), other.g4Tracks_.end()); } @@ -53,10 +54,15 @@ SimCluster SimCluster::mergeHitsFromCollection(const std::vector &in for (auto const &[detId, energy] : acc_energies) { ret.hits_.push_back(detId); ret.energies_.push_back(energy); + + if (!acc_fractions.empty()) { + auto it = acc_fractions.find(detId); + if (it != acc_fractions.end()) + ret.fractions_.push_back(it->second); + } } - for (auto const &[_, fraction] : acc_fractions) { - ret.fractions_.push_back(fraction); - } + assert(ret.fractions_.empty() || ret.fractions_.size() == ret.energies_.size()); + ret.nsimhits_ = ret.hits_.size(); return ret; From 2849a33dc113ec5287283b2a92595631e6a1eb60 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Thu, 30 Apr 2026 23:22:27 +0200 Subject: [PATCH 63/64] Keep old versions. --- SimDataFormats/CaloAnalysis/src/classes_def.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SimDataFormats/CaloAnalysis/src/classes_def.xml b/SimDataFormats/CaloAnalysis/src/classes_def.xml index 0bbc64f0d8d82..39700e3da5f97 100644 --- a/SimDataFormats/CaloAnalysis/src/classes_def.xml +++ b/SimDataFormats/CaloAnalysis/src/classes_def.xml @@ -58,6 +58,7 @@ + @@ -68,6 +69,7 @@ + From 5d24a35bef96535db9cfa5639e5226fff8e9ff76 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Fri, 1 May 2026 00:08:39 +0200 Subject: [PATCH 64/64] Address Theo's comments. --- .../CaloAnalysis/interface/SimCluster.h | 15 ++++++++------- .../MixingModule/python/caloTruthProducer_cfi.py | 8 +++++--- SimGeneral/MixingModule/python/digitizers_cfi.py | 3 --- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/SimDataFormats/CaloAnalysis/interface/SimCluster.h b/SimDataFormats/CaloAnalysis/interface/SimCluster.h index efbecd7a327a8..283db73bc7c8c 100644 --- a/SimDataFormats/CaloAnalysis/interface/SimCluster.h +++ b/SimDataFormats/CaloAnalysis/interface/SimCluster.h @@ -451,17 +451,17 @@ class SimCluster { void assertEnergies_() const { if (hits_.size() != energies_.size()) { - edm::LogWarning("SimCluster") << "energies (" + std::to_string(energies_.size()) + ") and hits (" + - std::to_string(hits_.size()) + ") must have the same size."; - std::abort(); + throw edm::Exception(edm::errors::StdException) << "energies (" + std::to_string(energies_.size()) + + ") and hits (" + std::to_string(hits_.size()) + + ") must have the same size."; } } void assertFractions_() const { if (hits_.size() != fractions_.size()) { - edm::LogWarning("SimCluster") << "fractions (" + std::to_string(fractions_.size()) + ") and hits (" + - std::to_string(hits_.size()) + ") must have the same size."; - std::abort(); + throw edm::Exception(edm::errors::StdException) << "fractions (" + std::to_string(fractions_.size()) + + ") and hits (" + std::to_string(hits_.size()) + + ") must have the same size."; } } @@ -505,7 +505,8 @@ class SimCluster { size_t i = 0; while (i < hits_.size()) { DetId id(hits_[i]); - const auto idx = detIndex_(static_cast(id.det())); + // const auto idx = detIndex_(static_cast(id.det())); + const auto idx = detIndex_(id.det()); const size_t begin = i; do { ++i; diff --git a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py index c38fc21b39a69..dfbe8c2c1d9cc 100644 --- a/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py +++ b/SimGeneral/MixingModule/python/caloTruthProducer_cfi.py @@ -56,7 +56,8 @@ # Phase-2 configuration (HGCAL only) # [FIXME: with the isBarrel check in associators it could contain all hits] from Configuration.Eras.Modifier_phase2_common_cff import phase2_common -phase2_common.toModify(caloParticles, +phase2_common.toModify( + caloParticles, doHGCAL=True, simHitCollections = cms.PSet( hgc = cms.VInputTag(*ph2_simHits["HGCAL"]), @@ -76,7 +77,7 @@ from Configuration.ProcessModifiers.run3_ecalclustering_cff import run3_ecalclustering run3_ecalclustering.toModify( - caloParticles, + caloParticles, simHitCollections = cms.PSet( ecal = cms.VInputTag( cms.InputTag('g4SimHits','EcalHitsEE'), @@ -88,7 +89,8 @@ from Configuration.ProcessModifiers.ticl_barrel_cff import ticl_barrel ticl_barrel.toModify( - caloParticles, + caloParticles, + doHGCAL=True, simHitCollections = cms.PSet( hgc = cms.VInputTag(*ph2_simHits["HGCAL"]), hcal = cms.VInputTag(*ph2_simHits["HCAL"]), diff --git a/SimGeneral/MixingModule/python/digitizers_cfi.py b/SimGeneral/MixingModule/python/digitizers_cfi.py index 0bf7980c84718..36563f8e9c3c9 100644 --- a/SimGeneral/MixingModule/python/digitizers_cfi.py +++ b/SimGeneral/MixingModule/python/digitizers_cfi.py @@ -54,7 +54,6 @@ def _rmCaloTruthIfPresent(pset): from Configuration.ProcessModifiers.premix_stage2_cff import premix_stage2 (fastSim & premix_stage2).toModify(theDigitizers, tracks = None) -(fastSim & premix_stage2).toModify(theDigitizers, _rmCaloTruthIfPresent) from SimCalorimetry.HGCalSimProducers.hgcalDigitizer_cfi import hgceeDigitizer, hgchebackDigitizer, hgchefrontDigitizer, HGCAL_noise_fC, HGCAL_noise_heback, HFNose_noise_fC, HGCAL_chargeCollectionEfficiencies, HGCAL_ileakParam_toUse, HGCAL_cceParams_toUse, HGCAL_noises @@ -76,7 +75,6 @@ def _rmCaloTruthIfPresent(pset): from Configuration.Eras.Modifier_run3_common_cff import run3_common # fastsim does not model castor (run3_common | fastSim).toModify(theDigitizers, castor = None) -(run3_common | fastSim).toModify(theDigitizers, _rmCaloTruthIfPresent) from SimGeneral.MixingModule.ecalTimeDigitizer_cfi import ecalTimeDigitizer from Configuration.Eras.Modifier_phase2_timing_cff import phase2_timing @@ -120,7 +118,6 @@ def _rmCaloTruthIfPresent(pset): from Configuration.Eras.Modifier_phase2_tracker_cff import phase2_tracker (phase2_tracker | fastSim).toModify(theDigitizers, strip = None) -(phase2_tracker | fastSim).toModify(theDigitizers, _rmCaloTruthIfPresent) theDigitizersValid = cms.PSet(theDigitizers) theDigitizers.mergedtruth.select.signalOnlyTP = True