From c2c400579ba70b29ef77cbb4ac1c3bbb0ba363b9 Mon Sep 17 00:00:00 2001 From: Seungjin Yang Date: Thu, 15 Oct 2020 22:43:52 +0900 Subject: [PATCH] Added GEM offlien DQM. --- .../DQMOfflineCosmics_SecondStep_cff.py | 7 + .../python/DQMOfflineCosmics_cff.py | 6 + .../DQMOfflineHeavyIons_SecondStep_cff.py | 6 + .../python/DQMOfflineHeavyIons_cff.py | 6 + .../python/DQMOffline_SecondStep_cff.py | 10 + .../Configuration/python/DQMOffline_cff.py | 10 + .../Muon/interface/GEMEfficiencyAnalyzer.h | 63 ++++ .../Muon/interface/GEMEfficiencyHarvester.h | 32 ++ DQMOffline/Muon/interface/GEMOfflineDQMBase.h | 179 ++++++++++ DQMOffline/Muon/interface/GEMOfflineMonitor.h | 34 ++ .../Muon/python/gemEfficiencyAnalyzer_cfi.py | 62 ++++ .../Muon/python/gemEfficiencyHarvester_cfi.py | 13 + .../Muon/python/gem_dqm_offline_client_cff.py | 7 + .../Muon/python/gem_dqm_offline_source_cff.py | 9 + DQMOffline/Muon/src/GEMEfficiencyAnalyzer.cc | 281 +++++++++++++++ DQMOffline/Muon/src/GEMEfficiencyHarvester.cc | 333 ++++++++++++++++++ DQMOffline/Muon/src/GEMOfflineDQMBase.cc | 91 +++++ DQMOffline/Muon/src/GEMOfflineMonitor.cc | 126 +++++++ DQMOffline/Muon/src/SealModule.cc | 6 + 19 files changed, 1281 insertions(+) create mode 100644 DQMOffline/Muon/interface/GEMEfficiencyAnalyzer.h create mode 100644 DQMOffline/Muon/interface/GEMEfficiencyHarvester.h create mode 100644 DQMOffline/Muon/interface/GEMOfflineDQMBase.h create mode 100644 DQMOffline/Muon/interface/GEMOfflineMonitor.h create mode 100644 DQMOffline/Muon/python/gemEfficiencyAnalyzer_cfi.py create mode 100644 DQMOffline/Muon/python/gemEfficiencyHarvester_cfi.py create mode 100644 DQMOffline/Muon/python/gem_dqm_offline_client_cff.py create mode 100644 DQMOffline/Muon/python/gem_dqm_offline_source_cff.py create mode 100644 DQMOffline/Muon/src/GEMEfficiencyAnalyzer.cc create mode 100644 DQMOffline/Muon/src/GEMEfficiencyHarvester.cc create mode 100644 DQMOffline/Muon/src/GEMOfflineDQMBase.cc create mode 100644 DQMOffline/Muon/src/GEMOfflineMonitor.cc diff --git a/DQMOffline/Configuration/python/DQMOfflineCosmics_SecondStep_cff.py b/DQMOffline/Configuration/python/DQMOfflineCosmics_SecondStep_cff.py index 69f0186b323d3..e9a5d70975785 100644 --- a/DQMOffline/Configuration/python/DQMOfflineCosmics_SecondStep_cff.py +++ b/DQMOffline/Configuration/python/DQMOfflineCosmics_SecondStep_cff.py @@ -11,6 +11,7 @@ from DQM.DTMonitorClient.dtDQMOfflineClients_Cosmics_cff import * from DQM.RPCMonitorClient.RPCTier0Client_cff import * from DQM.CSCMonitorModule.csc_dqm_offlineclient_cosmics_cff import * +from DQMOffline.Muon.gem_dqm_offline_client_cff import * from DQMServices.Components.DQMFEDIntegrityClient_cff import * DQMNone = cms.Sequence() @@ -27,6 +28,12 @@ DQMOfflineCosmics_SecondStepMuonDPG = cms.Sequence( dtClientsCosmics * rpcTier0Client * cscOfflineCosmicsClients ) + +from Configuration.Eras.Modifier_run3_GEM_cff import run3_GEM +_run3_GEM_DQMOfflineCosmics_SecondStepMuonDPG = DQMOfflineCosmics_SecondStepMuonDPG.copy() +_run3_GEM_DQMOfflineCosmics_SecondStepMuonDPG += gemClients +run3_GEM.toReplaceWith(DQMOfflineCosmics_SecondStepMuonDPG, _run3_GEM_DQMOfflineCosmics_SecondStepMuonDPG) + DQMOfflineCosmics_SecondStepFED = cms.Sequence( dqmFEDIntegrityClient ) DQMOfflineCosmics_SecondStep_PreDPG = cms.Sequence( diff --git a/DQMOffline/Configuration/python/DQMOfflineCosmics_cff.py b/DQMOffline/Configuration/python/DQMOfflineCosmics_cff.py index f897b2f46598f..ab141f80d108d 100644 --- a/DQMOffline/Configuration/python/DQMOfflineCosmics_cff.py +++ b/DQMOffline/Configuration/python/DQMOfflineCosmics_cff.py @@ -11,6 +11,7 @@ from DQM.DTMonitorModule.dtDQMOfflineSources_Cosmics_cff import * from DQM.RPCMonitorClient.RPCTier0Source_cff import * from DQM.CSCMonitorModule.csc_dqm_sourceclient_offline_cff import * +from DQMOffline.Muon.gem_dqm_offline_source_cff import * from DQM.EcalPreshowerMonitorModule.es_dqm_source_offline_cosmic_cff import * from DQM.CastorMonitor.castor_dqm_sourceclient_offline_cff import * @@ -33,6 +34,11 @@ rpcTier0Source * cscSources ) +from Configuration.Eras.Modifier_run3_GEM_cff import run3_GEM +_run3_GEM_DQMOfflineCosmicsMuonDPG = DQMOfflineCosmicsMuonDPG.copy() +_run3_GEM_DQMOfflineCosmicsMuonDPG += gemSources +run3_GEM.toReplaceWith(DQMOfflineCosmicsMuonDPG, _run3_GEM_DQMOfflineCosmicsMuonDPG) + DQMOfflineCosmicsCASTOR = cms.Sequence( castorSources ) DQMOfflineCosmicsPreDPG = cms.Sequence( DQMOfflineCosmicsDCS * diff --git a/DQMOffline/Configuration/python/DQMOfflineHeavyIons_SecondStep_cff.py b/DQMOffline/Configuration/python/DQMOfflineHeavyIons_SecondStep_cff.py index ff609f3beb87f..be01b91bf9392 100644 --- a/DQMOffline/Configuration/python/DQMOfflineHeavyIons_SecondStep_cff.py +++ b/DQMOffline/Configuration/python/DQMOfflineHeavyIons_SecondStep_cff.py @@ -11,6 +11,7 @@ from DQM.DTMonitorClient.dtDQMOfflineClients_cff import * from DQM.RPCMonitorClient.RPCTier0Client_cff import * from DQM.CSCMonitorModule.csc_dqm_offlineclient_collisions_cff import * +from DQMOffline.Muon.gem_dqm_offline_client_cff import * from DQMServices.Components.DQMFEDIntegrityClient_cff import * DQMNone = cms.Sequence() @@ -28,6 +29,11 @@ rpcTier0Client * cscOfflineCollisionsClients ) +from Configuration.Eras.Modifier_run3_GEM_cff import run3_GEM +_run3_GEM_DQMOfflineHeavyIons_SecondStepMuonDPG = DQMOfflineHeavyIons_SecondStepMuonDPG.copy() +_run3_GEM_DQMOfflineHeavyIons_SecondStepMuonDPG += gemClients +run3_GEM.toReplaceWith(DQMOfflineHeavyIons_SecondStepMuonDPG, _run3_GEM_DQMOfflineHeavyIons_SecondStepMuonDPG) + DQMOfflineHeavyIons_SecondStepFED = cms.Sequence( dqmFEDIntegrityClient ) DQMOfflineHeavyIons_SecondStep_PreDPG = cms.Sequence( diff --git a/DQMOffline/Configuration/python/DQMOfflineHeavyIons_cff.py b/DQMOffline/Configuration/python/DQMOfflineHeavyIons_cff.py index 1ef2794f77357..75de40d2cd6f9 100644 --- a/DQMOffline/Configuration/python/DQMOfflineHeavyIons_cff.py +++ b/DQMOffline/Configuration/python/DQMOfflineHeavyIons_cff.py @@ -13,6 +13,7 @@ from DQM.DTMonitorModule.dtDQMOfflineSources_HI_cff import * from DQM.RPCMonitorClient.RPCTier0Source_cff import * from DQM.CSCMonitorModule.csc_dqm_sourceclient_offline_cff import * +from DQMOffline.Muon.gem_dqm_offline_source_cff import * from DQM.BeamMonitor.AlcaBeamMonitorHeavyIons_cff import * DQMNone = cms.Sequence() @@ -37,6 +38,11 @@ rpcTier0Source * cscSources ) +from Configuration.Eras.Modifier_run3_GEM_cff import run3_GEM +_run3_GEM_DQMOfflineHeavyIonsMuonDPG = DQMOfflineHeavyIonsMuonDPG.copy() +_run3_GEM_DQMOfflineHeavyIonsMuonDPG += gemSources +run3_GEM.toReplaceWith(DQMOfflineHeavyIonsMuonDPG, _run3_GEM_DQMOfflineHeavyIonsMuonDPG) + DQMOfflineHeavyIonsPreDPG = cms.Sequence( DQMOfflineHeavyIonsDCS * DQMOfflineHeavyIonsL1T * DQMOfflineHeavyIonsEcal * diff --git a/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py b/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py index fdfc23ae61a91..870ca95a4a9c6 100644 --- a/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py +++ b/DQMOffline/Configuration/python/DQMOffline_SecondStep_cff.py @@ -10,6 +10,7 @@ from DQM.DTMonitorClient.dtDQMOfflineClients_cff import * from DQM.RPCMonitorClient.RPCTier0Client_cff import * from DQM.CSCMonitorModule.csc_dqm_offlineclient_collisions_cff import * +from DQMOffline.Muon.gem_dqm_offline_client_cff import * from DQMOffline.Hcal.HcalDQMOfflinePostProcessor_cff import * from DQM.HcalTasks.OfflineHarvestingSequence_pp import * from DQMServices.Components.DQMFEDIntegrityClient_cff import * @@ -29,6 +30,11 @@ rpcTier0Client * cscOfflineCollisionsClients ) +from Configuration.Eras.Modifier_run3_GEM_cff import run3_GEM +_run3_GEM_DQMOffline_SecondStepMuonDPG = DQMOffline_SecondStepMuonDPG.copy() +_run3_GEM_DQMOffline_SecondStepMuonDPG += gemClients +run3_GEM.toReplaceWith(DQMOffline_SecondStepMuonDPG, _run3_GEM_DQMOffline_SecondStepMuonDPG) + DQMOffline_SecondStepHcal = cms.Sequence( hcalOfflineHarvesting ) DQMOffline_SecondStepHcal2 = cms.Sequence( HcalDQMOfflinePostProcessor ) @@ -189,6 +195,10 @@ muonQualityTests ) +_run3_GEM_DQMHarvestMuon = DQMHarvestMuon.copy() +_run3_GEM_DQMHarvestMuon += gemClients +run3_GEM.toReplaceWith(DQMHarvestMuon, _run3_GEM_DQMHarvestMuon) + DQMHarvestEcal = cms.Sequence( ecal_dqm_client_offline * es_dqm_client_offline ) diff --git a/DQMOffline/Configuration/python/DQMOffline_cff.py b/DQMOffline/Configuration/python/DQMOffline_cff.py index a9d7a566cc415..408e7af59621f 100644 --- a/DQMOffline/Configuration/python/DQMOffline_cff.py +++ b/DQMOffline/Configuration/python/DQMOffline_cff.py @@ -14,6 +14,7 @@ from DQM.DTMonitorModule.dtDQMOfflineSources_cff import * from DQM.RPCMonitorClient.RPCTier0Source_cff import * from DQM.CSCMonitorModule.csc_dqm_sourceclient_offline_cff import * +from DQMOffline.Muon.gem_dqm_offline_source_cff import * from DQM.CastorMonitor.castor_dqm_sourceclient_offline_cff import * from DQM.CTPPS.ctppsDQM_cff import * from DQM.SiTrackerPhase2.Phase2TrackerDQMFirstStep_cff import * @@ -60,6 +61,11 @@ rpcTier0Source * cscSources ) +from Configuration.Eras.Modifier_run3_GEM_cff import run3_GEM +_run3_GEM_DQMOfflineMuonDPG = DQMOfflineMuonDPG.copy() +_run3_GEM_DQMOfflineMuonDPG += gemSources +run3_GEM.toReplaceWith(DQMOfflineMuonDPG, _run3_GEM_DQMOfflineMuonDPG) + DQMOfflineCASTOR = cms.Sequence( castorSources ) DQMOfflineCTPPS = cms.Sequence( ctppsDQMOfflineSource ) @@ -221,6 +227,10 @@ muonMonitors ) +_run3_GEM_DQMOfflineMuon = DQMOfflineMuon.copy() +_run3_GEM_DQMOfflineMuon += gemSources +run3_GEM.toReplaceWith(DQMOfflineMuon, _run3_GEM_DQMOfflineMuon) + #Taus not created in pp conditions for HI from Configuration.Eras.Modifier_pp_on_AA_2018_cff import pp_on_AA_2018 _DQMOfflineTAU = cms.Sequence() diff --git a/DQMOffline/Muon/interface/GEMEfficiencyAnalyzer.h b/DQMOffline/Muon/interface/GEMEfficiencyAnalyzer.h new file mode 100644 index 0000000000000..09c82469535ef --- /dev/null +++ b/DQMOffline/Muon/interface/GEMEfficiencyAnalyzer.h @@ -0,0 +1,63 @@ +#ifndef DQMOffline_Muon_GEMEfficiencyAnalyzer_h +#define DQMOffline_Muon_GEMEfficiencyAnalyzer_h + +#include "DQMOffline/Muon/interface/GEMOfflineDQMBase.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "DataFormats/GEMRecHit/interface/GEMRecHitCollection.h" +#include "DataFormats/PatCandidates/interface/Muon.h" +#include "DataFormats/MuonReco/interface/MuonSelectors.h" +#include "Geometry/GEMGeometry/interface/GEMGeometry.h" +#include "RecoMuon/TrackingTools/interface/MuonServiceProxy.h" + +class GEMEfficiencyAnalyzer : public GEMOfflineDQMBase { +public: + explicit GEMEfficiencyAnalyzer(const edm::ParameterSet &); + ~GEMEfficiencyAnalyzer() override; + +protected: + void bookHistograms(DQMStore::IBooker &, edm::Run const &, edm::EventSetup const &) override; + void analyze(const edm::Event &event, const edm::EventSetup &eventSetup) override; + +private: + void bookDetectorOccupancy( + DQMStore::IBooker &, const GEMStation *, const MEMapKey1 &, const TString &, const TString &); + void bookOccupancy(DQMStore::IBooker &, const MEMapKey2 &, const TString &, const TString &); + void bookResolution(DQMStore::IBooker &, const MEMapKey3 &, const TString &, const TString &); + + const GEMRecHit *findMatchedHit(const float, const GEMRecHitCollection::range &); + + edm::EDGetTokenT rechit_token_; + edm::EDGetTokenT > muon_token_; + + MuonServiceProxy *muon_service_; + + bool use_global_muon_; + float residual_x_cut_; + + std::vector pt_binning_; + int eta_nbins_; + double eta_low_; + double eta_up_; + + std::string folder_; + + TString title_; + TString matched_title_; + + MEMap1 me_detector_; + MEMap1 me_detector_matched_; + + MEMap2 me_muon_pt_; + MEMap2 me_muon_eta_; + MEMap2 me_muon_pt_matched_; + MEMap2 me_muon_eta_matched_; + + MEMap3 me_residual_x_; // local + MEMap3 me_residual_y_; // local + MEMap3 me_residual_phi_; // global + MEMap3 me_pull_x_; + MEMap3 me_pull_y_; +}; + +#endif // DQMOffline_Muon_GEMEfficiencyAnalyzer_h diff --git a/DQMOffline/Muon/interface/GEMEfficiencyHarvester.h b/DQMOffline/Muon/interface/GEMEfficiencyHarvester.h new file mode 100644 index 0000000000000..24f3889e9fb11 --- /dev/null +++ b/DQMOffline/Muon/interface/GEMEfficiencyHarvester.h @@ -0,0 +1,32 @@ +#ifndef DQMOffline_Muon_GEMEfficiencyHarvester_h +#define DQMOffline_Muon_GEMEfficiencyHarvester_h + +#include "DQMServices/Core/interface/DQMEDHarvester.h" +#include "DQMServices/Core/interface/DQMStore.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" + +#include +#include + +class GEMEfficiencyHarvester : public DQMEDHarvester { +public: + GEMEfficiencyHarvester(const edm::ParameterSet&); + ~GEMEfficiencyHarvester() override; + void dqmEndJob(DQMStore::IBooker&, DQMStore::IGetter&) override; + +private: + TProfile* computeEfficiency(const TH1F*, const TH1F*, const char*, const char*, const double confidence_level = 0.683); + + TH2F* computeEfficiency(const TH2F*, const TH2F*, const char*, const char*); + + std::vector splitString(std::string, const std::string); + std::tuple parseResidualName(std::string, const std::string); + + void doEfficiency(DQMStore::IBooker&, DQMStore::IGetter&); + void doResolution(DQMStore::IBooker&, DQMStore::IGetter&, const std::string); + + std::string folder_; + std::string log_category_; +}; + +#endif // DQMOffline_Muon_GEMEfficiencyHarvester_h diff --git a/DQMOffline/Muon/interface/GEMOfflineDQMBase.h b/DQMOffline/Muon/interface/GEMOfflineDQMBase.h new file mode 100644 index 0000000000000..dbd7a29252243 --- /dev/null +++ b/DQMOffline/Muon/interface/GEMOfflineDQMBase.h @@ -0,0 +1,179 @@ +#ifndef DQMOffline_Muon_GEMOfflineDQMBase_h +#define DQMOffline_Muon_GEMOfflineDQMBase_h + +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "DQMServices/Core/interface/DQMEDAnalyzer.h" +#include "DQMServices/Core/interface/DQMStore.h" +#include "CondFormats/GEMObjects/interface/GEMeMap.h" +#include "DataFormats/MuonDetId/interface/GEMDetId.h" +#include "Geometry/GEMGeometry/interface/GEMGeometry.h" + +class GEMOfflineDQMBase : public DQMEDAnalyzer { +public: + explicit GEMOfflineDQMBase(const edm::ParameterSet&); + + typedef std::tuple MEMapKey1; // (region, station) + typedef std::tuple MEMapKey2; // (region, station, is odd superchamber) + typedef std::tuple MEMapKey3; // (region, station, is odd superchamber, ieta) + typedef std::map MEMap1; + typedef std::map MEMap2; + typedef std::map MEMap3; + + inline int getVFATNumber(const int, const int, const int); + inline int getVFATNumberByStrip(const int, const int, const int); + inline int getMaxVFAT(const int); + inline int getDetOccXBin(const int, const int, const int); + + int getDetOccXBin(const GEMDetId&, const edm::ESHandle&); + void setDetLabelsVFAT(MonitorElement*, const GEMStation*); + void setDetLabelsEta(MonitorElement*, const GEMStation*); + // the number of eta partitions per GEMChamber + int getNumEtaPartitions(const GEMStation*); + + template + TString convertKeyToStr(const AnyKey& key); + + template + void fillME(std::map&, const AnyKey&, const float); + + template + void fillME(std::map&, const AnyKey&, const float, const float y); + + template + inline bool checkRefs(const std::vector&); + + std::string log_category_; + + class BookingHelper { + public: + BookingHelper(DQMStore::IBooker& ibooker, const TString& name_suffix, const TString& title_suffix) + : ibooker_(&ibooker), name_suffix_(name_suffix), title_suffix_(title_suffix) {} + + ~BookingHelper() {} + + MonitorElement* book1D(TString name, + TString title, + int nbinsx, + double xlow, + double xup, + TString x_title = "", + TString y_title = "Entries") { + name += name_suffix_; + title += title_suffix_ + ";" + x_title + ";" + y_title; + return ibooker_->book1D(name, title, nbinsx, xlow, xup); + } + + MonitorElement* book1D(TString name, + TString title, + std::vector& x_binning, + TString x_title = "", + TString y_title = "Entries") { + name += name_suffix_; + title += title_suffix_ + ";" + x_title + ";" + y_title; + TH1F* h_obj = new TH1F(name, title, x_binning.size() - 1, &x_binning[0]); + return ibooker_->book1D(name, h_obj); + } + + MonitorElement* book2D(TString name, + TString title, + int nbinsx, + double xlow, + double xup, + int nbinsy, + double ylow, + double yup, + TString x_title = "", + TString y_title = "") { + name += name_suffix_; + title += title_suffix_ + ";" + x_title + ";" + y_title; + return ibooker_->book2D(name, title, nbinsx, xlow, xup, nbinsy, ylow, yup); + } + + private: + DQMStore::IBooker* ibooker_; + const TString name_suffix_; + const TString title_suffix_; + }; // BookingHelper +}; + +inline int GEMOfflineDQMBase::getMaxVFAT(const int station) { + if (station == 1) + return GEMeMap::maxVFatGE11_; + else if (station == 2) + return GEMeMap::maxVFatGE21_; + else + return -1; +} + +inline int GEMOfflineDQMBase::getVFATNumber(const int station, const int ieta, const int vfat_phi) { + const int max_vfat = getMaxVFAT(station); + return max_vfat * (ieta - 1) + vfat_phi; +} + +inline int GEMOfflineDQMBase::getVFATNumberByStrip(const int station, const int ieta, const int strip) { + const int vfat_phi = (strip % GEMeMap::maxChan_) ? strip / GEMeMap::maxChan_ + 1 : strip / GEMeMap::maxChan_; + return getVFATNumber(station, ieta, vfat_phi); +}; + +inline int GEMOfflineDQMBase::getDetOccXBin(const int chamber, const int layer, const int n_chambers) { + return n_chambers * (chamber - 1) + layer; +} + +template +inline bool GEMOfflineDQMBase::checkRefs(const std::vector& refs) { + if (refs.empty()) + return false; + if (refs.front() == nullptr) + return false; + return true; +} + +template +TString GEMOfflineDQMBase::convertKeyToStr(const AnyKey& key) { + if constexpr (std::is_same_v) { + return TString::Format("Region %d, Station %d.", std::get<0>(key), std::get<1>(key)); + + } else if constexpr (std::is_same_v) { + const char* superchamber_type = std::get<2>(key) ? "Odd" : "Even"; + return TString::Format( + "Region %d, Station %d, %s Superchamber", std::get<0>(key), std::get<1>(key), superchamber_type); + + } else if constexpr (std::is_same_v) { + const char* superchamber_type = std::get<2>(key) ? "Odd" : "Even"; + return TString::Format("Region %d, Station %d, %s Superchamber, Roll %d", + std::get<0>(key), + std::get<1>(key), + superchamber_type, + std::get<3>(key)); + + } else { + return TString::Format("unknown key type: %s", typeid(key).name()); + } +} + +template +void GEMOfflineDQMBase::fillME(std::map& me_map, const AnyKey& key, const float x) { + if (me_map.find(key) == me_map.end()) { + const TString&& key_str = convertKeyToStr(key); + edm::LogError(log_category_) << "got invalid key: " << key_str << std::endl; + + } else { + me_map[key]->Fill(x); + } +} + +template +void GEMOfflineDQMBase::fillME(std::map& me_map, + const AnyKey& key, + const float x, + const float y) { + if (me_map.find(key) == me_map.end()) { + const TString&& key_str = convertKeyToStr(key); + edm::LogError(log_category_) << "got invalid key: " << key_str << std::endl; + + } else { + me_map[key]->Fill(x, y); + } +} + +#endif // DQMOffline_Muon_GEMOfflineDQMBase_h diff --git a/DQMOffline/Muon/interface/GEMOfflineMonitor.h b/DQMOffline/Muon/interface/GEMOfflineMonitor.h new file mode 100644 index 0000000000000..19063b5d5f77a --- /dev/null +++ b/DQMOffline/Muon/interface/GEMOfflineMonitor.h @@ -0,0 +1,34 @@ +#ifndef DQMOffline_Muon_GEMOfflineMonitor_h +#define DQMOffline_Muon_GEMOfflineMonitor_h + +#include "DQMOffline/Muon/interface/GEMOfflineDQMBase.h" +#include "FWCore/Utilities/interface/EDGetToken.h" +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "DataFormats/GEMDigi/interface/GEMDigiCollection.h" +#include "DataFormats/GEMRecHit/interface/GEMRecHitCollection.h" + +class GEMOfflineMonitor : public GEMOfflineDQMBase { +public: + explicit GEMOfflineMonitor(const edm::ParameterSet &); + ~GEMOfflineMonitor() override; + static void fillDescriptions(edm::ConfigurationDescriptions &); + +protected: + void bookHistograms(DQMStore::IBooker &, edm::Run const &, edm::EventSetup const &) override; + void analyze(const edm::Event &event, const edm::EventSetup &eventSetup) override; + +private: + void bookDetectorOccupancy( + DQMStore::IBooker &, const GEMStation *, const MEMapKey1 &, const TString &, const TString &); + + edm::EDGetTokenT digi_token_; + edm::EDGetTokenT rechit_token_; + + std::string log_category_; + + MEMap1 me_digi_det_; + MEMap1 me_hit_det_; +}; + +#endif // DQMOffline_Muon_GEMOfflineMonitor_h diff --git a/DQMOffline/Muon/python/gemEfficiencyAnalyzer_cfi.py b/DQMOffline/Muon/python/gemEfficiencyAnalyzer_cfi.py new file mode 100644 index 0000000000000..c284afb7ab195 --- /dev/null +++ b/DQMOffline/Muon/python/gemEfficiencyAnalyzer_cfi.py @@ -0,0 +1,62 @@ +import FWCore.ParameterSet.Config as cms +from DQMServices.Core.DQMEDAnalyzer import DQMEDAnalyzer +from RecoMuon.TrackingTools.MuonServiceProxy_cff import MuonServiceProxy + + +gemOfflineDQMTightGlbMuons = cms.EDFilter("MuonSelector", + src = cms.InputTag('muons'), + cut = cms.string( + '(pt > 20)' + '&& isGlobalMuon' + '&& globalTrack.isNonnull' + '&& passed(\'CutBasedIdTight\')' + ), + filter = cms.bool(False) +) + + +gemOfflineDQMStaMuons = cms.EDFilter("MuonSelector", + src = cms.InputTag('muons'), + cut = cms.string( + '(pt > 20)' + '&& isStandAloneMuon' + '&& outerTrack.isNonnull' + ), + filter = cms.bool(False) +) + + +gemEfficiencyAnalyzerTight = DQMEDAnalyzer('GEMEfficiencyAnalyzer', + MuonServiceProxy, + muonTag = cms.InputTag('gemOfflineDQMTightGlbMuons'), + recHitTag = cms.InputTag('gemRecHits'), + residualXCut = cms.double(5.0), + ptBinning = cms.untracked.vdouble(20. ,30., 40., 50., 60., 70., 80., 90., 100., 120., 140., 200.), + etaNbins = cms.untracked.int32(7), + etaLow = cms.untracked.double(1.5), + etaUp = cms.untracked.double(2.2), + useGlobalMuon = cms.untracked.bool(True), + folder = cms.untracked.string('GEM/GEMEfficiency/TightGlobalMuon'), + logCategory = cms.untracked.string('GEMEfficiencyAnalyzerTight'), +) + + +gemEfficiencyAnalyzerSTA = gemEfficiencyAnalyzerTight.clone() +gemEfficiencyAnalyzerSTA.muonTag = cms.InputTag("gemOfflineDQMStaMuons") +gemEfficiencyAnalyzerSTA.useGlobalMuon = cms.untracked.bool(False) +gemEfficiencyAnalyzerSTA.folder = cms.untracked.string('GEM/GEMEfficiency/StandaloneMuon') +gemEfficiencyAnalyzerSTA.logCategory = cms.untracked.string('GEMEfficiencyAnalyzerSTA') + + +from Configuration.Eras.Modifier_phase2_GEM_cff import phase2_GEM +phase2_GEM.toModify(gemEfficiencyAnalyzerTight, etaNbins=cms.untracked.int32(15), etaHigh=cms.untracked.double(3.0)) +phase2_GEM.toModify(gemEfficiencyAnalyzerSTA, etaNbins=cms.untracked.int32(15), etaHigh=cms.untracked.double(3.0)) + + +gemEfficiencyAnalyzerTightSeq = cms.Sequence( + cms.ignore(gemOfflineDQMTightGlbMuons) * + gemEfficiencyAnalyzerTight) + +gemEfficiencyAnalyzerSTASeq = cms.Sequence( + cms.ignore(gemOfflineDQMStaMuons) * + gemEfficiencyAnalyzerSTA) diff --git a/DQMOffline/Muon/python/gemEfficiencyHarvester_cfi.py b/DQMOffline/Muon/python/gemEfficiencyHarvester_cfi.py new file mode 100644 index 0000000000000..ff49777cec015 --- /dev/null +++ b/DQMOffline/Muon/python/gemEfficiencyHarvester_cfi.py @@ -0,0 +1,13 @@ +import FWCore.ParameterSet.Config as cms + +from DQMServices.Core.DQMEDHarvester import DQMEDHarvester + +gemEfficiencyHarvesterTight = DQMEDHarvester('GEMEfficiencyHarvester', + folder = cms.untracked.string('GEM/GEMEfficiency/TightGlobalMuon'), + logCategory = cms.untracked.string('GEMEfficiencyHarvesterTight') +) + +gemEfficiencyHarvesterSTA = DQMEDHarvester('GEMEfficiencyHarvester', + folder = cms.untracked.string('GEM/GEMEfficiency/StandaloneMuon'), + logCategory = cms.untracked.string('GEMEfficiencyHarvesterSTA') +) diff --git a/DQMOffline/Muon/python/gem_dqm_offline_client_cff.py b/DQMOffline/Muon/python/gem_dqm_offline_client_cff.py new file mode 100644 index 0000000000000..8d89e87148dec --- /dev/null +++ b/DQMOffline/Muon/python/gem_dqm_offline_client_cff.py @@ -0,0 +1,7 @@ +import FWCore.ParameterSet.Config as cms + +from DQMOffline.Muon.gemEfficiencyHarvester_cfi import * + +gemClients = cms.Sequence( + gemEfficiencyHarvesterTight * + gemEfficiencyHarvesterSTA) diff --git a/DQMOffline/Muon/python/gem_dqm_offline_source_cff.py b/DQMOffline/Muon/python/gem_dqm_offline_source_cff.py new file mode 100644 index 0000000000000..0af44227fde1e --- /dev/null +++ b/DQMOffline/Muon/python/gem_dqm_offline_source_cff.py @@ -0,0 +1,9 @@ +import FWCore.ParameterSet.Config as cms + +from DQMOffline.Muon.gemOfflineMonitor_cfi import * +from DQMOffline.Muon.gemEfficiencyAnalyzer_cfi import * + +gemSources = cms.Sequence( + gemOfflineMonitor * + gemEfficiencyAnalyzerTightSeq * + gemEfficiencyAnalyzerSTASeq) diff --git a/DQMOffline/Muon/src/GEMEfficiencyAnalyzer.cc b/DQMOffline/Muon/src/GEMEfficiencyAnalyzer.cc new file mode 100644 index 0000000000000..2cd3e91446cc4 --- /dev/null +++ b/DQMOffline/Muon/src/GEMEfficiencyAnalyzer.cc @@ -0,0 +1,281 @@ +#include "DQMOffline/Muon/interface/GEMEfficiencyAnalyzer.h" +#include "FWCore/Framework/interface/ConsumesCollector.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "TrackingTools/TransientTrack/interface/TransientTrackBuilder.h" +#include "TrackingTools/Records/interface/TransientTrackRecord.h" +#include "DataFormats/Math/interface/deltaPhi.h" + +GEMEfficiencyAnalyzer::GEMEfficiencyAnalyzer(const edm::ParameterSet& pset) : GEMOfflineDQMBase(pset) { + rechit_token_ = consumes(pset.getParameter("recHitTag")); + muon_token_ = consumes >(pset.getParameter("muonTag")); + + auto muon_service_parameter = pset.getParameter("ServiceParameters"); + muon_service_ = new MuonServiceProxy(muon_service_parameter, consumesCollector()); + + use_global_muon_ = pset.getUntrackedParameter("useGlobalMuon"); + + residual_x_cut_ = static_cast(pset.getParameter("residualXCut")); + + pt_binning_ = pset.getUntrackedParameter >("ptBinning"); + eta_nbins_ = pset.getUntrackedParameter("etaNbins"); + eta_low_ = pset.getUntrackedParameter("etaLow"); + eta_up_ = pset.getUntrackedParameter("etaUp"); + + folder_ = pset.getUntrackedParameter("folder"); + + title_ = (use_global_muon_ ? "Global Muon" : "Standalone Muon"); + matched_title_ = title_ + TString::Format(" (|x_{Muon} - x_{Hit}| < %.1f)", residual_x_cut_); +} + +GEMEfficiencyAnalyzer::~GEMEfficiencyAnalyzer() {} + +void GEMEfficiencyAnalyzer::bookHistograms(DQMStore::IBooker& ibooker, + edm::Run const& run, + edm::EventSetup const& isetup) { + edm::ESHandle gem; + isetup.get().get(gem); + if (not gem.isValid()) { + edm::LogError(log_category_) << "GEMGeometry is invalid" << std::endl; + return; + } + + for (const GEMRegion* region : gem->regions()) { + const int region_number = region->region(); + const char* region_sign = region_number > 0 ? "+" : "-"; + + for (const GEMStation* station : region->stations()) { + const int station_number = station->station(); + + const MEMapKey1 key1{region_number, station_number}; + const auto&& station_name_suffix = TString::Format("_ge%s%d1", region_sign, station_number); + const auto&& station_title_suffix = TString::Format(" : GE %s%d/1", region_sign, station_number); + bookDetectorOccupancy(ibooker, station, key1, station_name_suffix, station_title_suffix); + + const int num_etas = getNumEtaPartitions(station); + + if (station_number == 1) { + for (const bool is_odd : {true, false}) { + std::tuple key2{region_number, station_number, is_odd}; + const TString&& parity_name_suffix = station_name_suffix + (is_odd ? "_odd" : "_even"); + const TString&& parity_title_suffix = + station_title_suffix + (is_odd ? ", Odd Superchamber" : ", Even Superchamber"); + bookOccupancy(ibooker, key2, parity_name_suffix, parity_title_suffix); + + for (int ieta = 1; ieta <= num_etas; ieta++) { + const TString&& ieta_name_suffix = parity_name_suffix + Form("_ieta%d", ieta); + const TString&& ieta_title_suffix = parity_title_suffix + Form(", i#eta = %d", ieta); + const MEMapKey3 key3{region_number, station_number, is_odd, ieta}; + bookResolution(ibooker, key3, ieta_name_suffix, ieta_title_suffix); + } // ieta + } // is_odd + + } else { + std::tuple key2{region_number, station_number, false}; + bookOccupancy(ibooker, key2, station_name_suffix, station_title_suffix); + + for (int ieta = 1; ieta <= num_etas; ieta++) { + const MEMapKey3 key3{region_number, station_number, false, ieta}; + const TString&& ieta_name_suffix = station_name_suffix + Form("_ieta%d", ieta); + const TString&& ieta_title_suffix = station_title_suffix + Form(", i#eta = %d", ieta); + bookResolution(ibooker, key3, ieta_name_suffix, ieta_title_suffix); + } // ieta + } + } // station + } // region +} + +void GEMEfficiencyAnalyzer::bookDetectorOccupancy(DQMStore::IBooker& ibooker, + const GEMStation* station, + const MEMapKey1& key, + const TString& name_suffix, + const TString& title_suffix) { + ibooker.setCurrentFolder(folder_ + "/Efficiency"); + BookingHelper helper(ibooker, name_suffix, title_suffix); + + const auto&& superchambers = station->superChambers(); + if (not checkRefs(superchambers)) { + edm::LogError(log_category_) << "failed to get a valid vector of GEMSuperChamber ptrs" << std::endl; + return; + } + + // the number of GEMChambers per GEMStation + const int num_ch = superchambers.size() * superchambers.front()->nChambers(); + // the number of eta partitions per GEMChamber + const int num_etas = getNumEtaPartitions(station); + + me_detector_[key] = helper.book2D("detector", title_, num_ch, 0.5, num_ch + 0.5, num_etas, 0.5, num_etas + 0.5); + + me_detector_matched_[key] = + helper.book2D("detector_matched", matched_title_, num_ch, 0.5, num_ch + 0.5, num_etas, 0.5, num_etas + 0.5); + + setDetLabelsEta(me_detector_[key], station); + setDetLabelsEta(me_detector_matched_[key], station); +} + +void GEMEfficiencyAnalyzer::bookOccupancy(DQMStore::IBooker& ibooker, + const MEMapKey2& key, + const TString& name_suffix, + const TString& title_suffix) { + ibooker.setCurrentFolder(folder_ + "/Efficiency"); + BookingHelper helper(ibooker, name_suffix, title_suffix); + + me_muon_pt_[key] = helper.book1D("muon_pt", title_, pt_binning_, "Muon p_{T} [GeV]"); + me_muon_eta_[key] = helper.book1D("muon_eta", title_, eta_nbins_, eta_low_, eta_up_, "Muon |#eta|"); + + me_muon_pt_matched_[key] = helper.book1D("muon_pt_matched", matched_title_, pt_binning_, "Muon p_{T} [GeV]"); + me_muon_eta_matched_[key] = + helper.book1D("muon_eta_matched", matched_title_, eta_nbins_, eta_low_, eta_up_, "Muon |#eta|"); +} + +void GEMEfficiencyAnalyzer::bookResolution(DQMStore::IBooker& ibooker, + const MEMapKey3& key, + const TString& name_suffix, + const TString& title_suffix) { + ibooker.setCurrentFolder(folder_ + "/Resolution"); + BookingHelper helper(ibooker, name_suffix, title_suffix); + + // NOTE Residual & Pull + me_residual_x_[key] = helper.book1D("residual_x", title_, 50, -5.0, 5.0, "Residual in Local X [cm]"); + me_residual_y_[key] = helper.book1D("residual_y", title_, 60, -12.0, 12.0, "Residual in Local Y [cm]"); + me_residual_phi_[key] = helper.book1D("residual_phi", title_, 80, -0.008, 0.008, "Residual in Global #phi [rad]"); + + me_pull_x_[key] = helper.book1D("pull_x", title_, 60, -3.0, 3.0, "Pull in Local X"); + me_pull_y_[key] = helper.book1D("pull_y", title_, 60, -3.0, 3.0, "Pull in Local Y"); +} + +void GEMEfficiencyAnalyzer::analyze(const edm::Event& event, const edm::EventSetup& setup) { + edm::Handle rechit_collection; + event.getByToken(rechit_token_, rechit_collection); + if (not rechit_collection.isValid()) { + edm::LogError(log_category_) << "GEMRecHitCollection is invalid" << std::endl; + return; + } + + edm::Handle > muon_view; + event.getByToken(muon_token_, muon_view); + if (not muon_view.isValid()) { + edm::LogError(log_category_) << "View is invalid" << std::endl; + } + + edm::ESHandle gem; + setup.get().get(gem); + if (not gem.isValid()) { + edm::LogError(log_category_) << "GEMGeometry is invalid" << std::endl; + return; + } + + edm::ESHandle transient_track_builder; + setup.get().get("TransientTrackBuilder", transient_track_builder); + if (not transient_track_builder.isValid()) { + edm::LogError(log_category_) << "TransientTrackRecord is invalid" << std::endl; + return; + } + + muon_service_->update(setup); + edm::ESHandle&& propagator = muon_service_->propagator("SteppingHelixPropagatorAny"); + if (not propagator.isValid()) { + edm::LogError(log_category_) << "Propagator is invalid" << std::endl; + return; + } + + for (const reco::Muon& muon : *muon_view) { + const reco::Track* track = nullptr; + + if (use_global_muon_ and muon.globalTrack().isNonnull()) { + track = muon.globalTrack().get(); + + } else if ((not use_global_muon_) and muon.outerTrack().isNonnull()) { + track = muon.outerTrack().get(); + } + + if (track == nullptr) { + edm::LogError(log_category_) << "failed to get muon track" << std::endl; + continue; + } + + const reco::TransientTrack&& transient_track = transient_track_builder->build(track); + if (not transient_track.isValid()) { + edm::LogInfo(log_category_) << "failed to build TransientTrack" << std::endl; + continue; + } + + for (const GEMEtaPartition* eta_partition : gem->etaPartitions()) { + // Skip propagation inn the opposite direction. + if (muon.eta() * eta_partition->id().region() < 0) + continue; + + const BoundPlane& bound_plane = eta_partition->surface(); + + const TrajectoryStateOnSurface&& tsos = + propagator->propagate(transient_track.outermostMeasurementState(), bound_plane); + if (not tsos.isValid()) { + continue; + } + + const LocalPoint&& tsos_local_pos = tsos.localPosition(); + const LocalPoint tsos_local_pos_2d(tsos_local_pos.x(), tsos_local_pos.y(), 0.0f); + if (not bound_plane.bounds().inside(tsos_local_pos_2d)) { + continue; + } + + const GEMDetId&& gem_id = eta_partition->id(); + + bool is_odd = gem_id.station() == 1 ? (gem_id.chamber() % 2 == 1) : false; + const std::tuple key1{gem_id.region(), gem_id.station()}; + const std::tuple key2{gem_id.region(), gem_id.station(), is_odd}; + const std::tuple key3{gem_id.region(), gem_id.station(), is_odd, gem_id.roll()}; + + const int chamber_bin = getDetOccXBin(gem_id, gem); + + fillME(me_detector_, key1, chamber_bin, gem_id.roll()); + fillME(me_muon_pt_, key2, muon.pt()); + fillME(me_muon_eta_, key2, std::fabs(muon.eta())); + + const GEMRecHit* matched_hit = findMatchedHit(tsos_local_pos.x(), rechit_collection->get(gem_id)); + if (matched_hit == nullptr) { + continue; + } + + fillME(me_detector_matched_, key1, chamber_bin, gem_id.roll()); + fillME(me_muon_pt_matched_, key2, muon.pt()); + fillME(me_muon_eta_matched_, key2, std::fabs(muon.eta())); + + const LocalPoint&& hit_local_pos = matched_hit->localPosition(); + const GlobalPoint&& hit_global_pos = eta_partition->toGlobal(hit_local_pos); + const GlobalPoint&& tsos_global_pos = tsos.globalPosition(); + + const float residual_x = tsos_local_pos.x() - hit_local_pos.x(); + const float residual_y = tsos_local_pos.y() - hit_local_pos.y(); + const float residual_phi = reco::deltaPhi(tsos_global_pos.barePhi(), hit_global_pos.barePhi()); + + const LocalError&& tsos_err = tsos.localError().positionError(); + const LocalError&& hit_err = matched_hit->localPositionError(); + + const float pull_x = residual_x / std::sqrt(tsos_err.xx() + hit_err.xx()); + const float pull_y = residual_y / std::sqrt(tsos_err.yy() + hit_err.yy()); + + fillME(me_residual_x_, key3, residual_x); + fillME(me_residual_y_, key3, residual_y); + fillME(me_residual_phi_, key3, residual_phi); + + fillME(me_pull_x_, key3, pull_x); + fillME(me_pull_y_, key3, pull_y); + } // GEMChamber + } // Muon +} + +const GEMRecHit* GEMEfficiencyAnalyzer::findMatchedHit(const float track_local_x, + const GEMRecHitCollection::range& range) { + float min_residual_x{residual_x_cut_}; + const GEMRecHit* closest_hit = nullptr; + + for (auto hit = range.first; hit != range.second; ++hit) { + float residual_x = std::fabs(track_local_x - hit->localPosition().x()); + if (residual_x <= min_residual_x) { + min_residual_x = residual_x; + closest_hit = &(*hit); + } + } + + return closest_hit; +} diff --git a/DQMOffline/Muon/src/GEMEfficiencyHarvester.cc b/DQMOffline/Muon/src/GEMEfficiencyHarvester.cc new file mode 100644 index 0000000000000..b014b207997e3 --- /dev/null +++ b/DQMOffline/Muon/src/GEMEfficiencyHarvester.cc @@ -0,0 +1,333 @@ +#include "DQMOffline/Muon/interface/GEMEfficiencyHarvester.h" + +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "TEfficiency.h" + +GEMEfficiencyHarvester::GEMEfficiencyHarvester(const edm::ParameterSet& pset) { + folder_ = pset.getUntrackedParameter("folder"); + log_category_ = pset.getUntrackedParameter("logCategory"); +} + +GEMEfficiencyHarvester::~GEMEfficiencyHarvester() {} + +TProfile* GEMEfficiencyHarvester::computeEfficiency( + const TH1F* passed, const TH1F* total, const char* name, const char* title, const double confidence_level) { + if (not TEfficiency::CheckConsistency(*passed, *total)) { + edm::LogError(log_category_) << "failed to pass TEfficiency::CheckConsistency. " << name << std::endl; + return nullptr; + } + + const TAxis* total_x = total->GetXaxis(); + + TProfile* eff_profile = new TProfile(name, title, total_x->GetNbins(), total_x->GetXmin(), total_x->GetXmax()); + eff_profile->GetXaxis()->SetTitle(total_x->GetTitle()); + eff_profile->GetYaxis()->SetTitle("#epsilon"); + + for (int bin = 1; bin < total->GetNbinsX(); bin++) { + double num_passed = passed->GetBinContent(bin); + double num_total = total->GetBinContent(bin); + + if (num_total < 1) { + eff_profile->SetBinEntries(bin, 0); + continue; + } + + double efficiency = num_passed / num_total; + + double lower_bound = TEfficiency::ClopperPearson(num_total, num_passed, confidence_level, false); + double upper_bound = TEfficiency::ClopperPearson(num_total, num_passed, confidence_level, true); + + double width = std::max(efficiency - lower_bound, upper_bound - efficiency); + double error = std::hypot(efficiency, width); + + eff_profile->SetBinContent(bin, efficiency); + eff_profile->SetBinError(bin, error); + eff_profile->SetBinEntries(bin, 1); + } + + return eff_profile; +} + +TH2F* GEMEfficiencyHarvester::computeEfficiency(const TH2F* passed, + const TH2F* total, + const char* name, + const char* title) { + if (not TEfficiency::CheckConsistency(*passed, *total)) { + edm::LogError(log_category_) << "failed to pass TEfficiency::CheckConsistency. " << name << std::endl; + return nullptr; + } + + TEfficiency eff(*passed, *total); + TH2F* eff_hist = dynamic_cast(eff.CreateHistogram()); + eff_hist->SetName(name); + eff_hist->SetTitle(title); + + const TAxis* total_x = total->GetXaxis(); + TAxis* eff_hist_x = eff_hist->GetXaxis(); + eff_hist_x->SetTitle(total_x->GetTitle()); + for (int bin = 1; bin <= total->GetNbinsX(); bin++) { + const char* label = total_x->GetBinLabel(bin); + eff_hist_x->SetBinLabel(bin, label); + } + + const TAxis* total_y = total->GetYaxis(); + TAxis* eff_hist_y = eff_hist->GetYaxis(); + eff_hist_y->SetTitle(total_y->GetTitle()); + for (int bin = 1; bin <= total->GetNbinsY(); bin++) { + const char* label = total_y->GetBinLabel(bin); + eff_hist_y->SetBinLabel(bin, label); + } + + return eff_hist; +} + +void GEMEfficiencyHarvester::doEfficiency(DQMStore::IBooker& ibooker, DQMStore::IGetter& igetter) { + const std::string efficiency_folder = folder_ + "/Efficiency/"; + ibooker.setCurrentFolder(efficiency_folder); + igetter.setCurrentFolder(efficiency_folder); + + std::map > me_pairs; + + const std::string matched = "_matched"; + + for (const std::string& name : igetter.getMEs()) { + const std::string fullpath = efficiency_folder + name; + const MonitorElement* me = igetter.get(fullpath); + if (me == nullptr) { + edm::LogError(log_category_) << "failed to get " << fullpath << std::endl; + continue; + } + + const bool is_matched = name.find(matched) != std::string::npos; + + std::string key = name; + if (is_matched) + key.erase(key.find(matched), matched.length()); + + if (me_pairs.find(key) == me_pairs.end()) { + me_pairs[key] = {nullptr, nullptr}; + } + + if (is_matched) + me_pairs[key].first = me; + else + me_pairs[key].second = me; + } + + for (auto&& [key, value] : me_pairs) { + const auto& [me_passed, me_total] = value; + if (me_passed == nullptr) { + edm::LogError(log_category_) << "numerator is missing. " << key << std::endl; + } + + if (me_total == nullptr) { + edm::LogError(log_category_) << "denominator is missing. " << key << std::endl; + continue; + } + + if (me_passed->kind() != me_total->kind()) { + edm::LogError(log_category_) << "inconsistency between kinds of passed and total" << key << std::endl; + continue; + } + + const std::string name = "eff_" + me_total->getName(); + const std::string title = me_passed->getTitle(); + + if (me_passed->kind() == MonitorElement::Kind::TH1F) { + TH1F* h_passed = me_passed->getTH1F(); + if (h_passed == nullptr) { + edm::LogError(log_category_) << "failed to get TH1F from passed " << key << std::endl; + continue; + } + h_passed->Sumw2(); + + TH1F* h_total = me_total->getTH1F(); + if (h_total == nullptr) { + edm::LogError(log_category_) << "failed to get TH1F from total" << key << std::endl; + continue; + } + h_total->Sumw2(); + + TProfile* eff = computeEfficiency(h_passed, h_total, name.c_str(), title.c_str()); + if (eff == nullptr) { + edm::LogError(log_category_) << "failed to compute the efficiency " << key << std::endl; + continue; + } + + ibooker.bookProfile(name, eff); + + } else if (me_passed->kind() == MonitorElement::Kind::TH2F) { + TH2F* h_passed = me_passed->getTH2F(); + if (h_passed == nullptr) { + edm::LogError(log_category_) << "failed to get TH1F from passed " << key << std::endl; + continue; + } + h_passed->Sumw2(); + + TH2F* h_total = me_total->getTH2F(); + if (h_total == nullptr) { + edm::LogError(log_category_) << "failed to get TH1F from total" << key << std::endl; + continue; + } + h_total->Sumw2(); + + TH2F* eff = computeEfficiency(h_passed, h_total, name.c_str(), title.c_str()); + if (eff == nullptr) { + edm::LogError(log_category_) << "failed to compute the efficiency " << key << std::endl; + continue; + } + + ibooker.book2D(name, eff); + + } else { + edm::LogError(log_category_) << "not implemented" << std::endl; + continue; + } + } // me_pairs +} + +std::vector GEMEfficiencyHarvester::splitString(std::string name, const std::string delimiter) { + std::vector tokens; + size_t delimiter_pos; + size_t delimiter_len = delimiter.length(); + while ((delimiter_pos = name.find('_')) != std::string::npos) { + tokens.push_back(name.substr(0, delimiter_pos)); + name.erase(0, delimiter_pos + delimiter_len); + } + tokens.push_back(name); + return tokens; +} + +std::tuple GEMEfficiencyHarvester::parseResidualName(const std::string org_name, + const std::string prefix) { + std::string name = org_name; + + // residual_x_ge-11_odd_ieta4 or residdual_x_ge+21_ieta3 + // residual_x: prefix + name.erase(name.find(prefix), prefix.length()); + name.erase(name.find("_ge"), 3); + + const std::vector&& tokens = splitString(name, "_"); + const size_t num_tokens = tokens.size(); + + if ((num_tokens != 2) and (num_tokens != 3)) { + return std::make_tuple("", -1, false, -1); + } + + // station != 1 + std::string region_sign = tokens.front().substr(0, 1); + + TString station_str = tokens.front().substr(1, 1); + TString ieta_str = tokens.back().substr(4, 1); + TString superchamber_str = (num_tokens == 3) ? tokens[1] : ""; + + int station = station_str.IsDigit() ? station_str.Atoi() : -1; + int ieta = ieta_str.IsDigit() ? ieta_str.Atoi() : -1; + + bool is_odd; + if (station == 1) { + if (superchamber_str.EqualTo("odd")) + is_odd = true; + else if (superchamber_str.EqualTo("even")) + is_odd = false; + else + return std::make_tuple("", -1, false, -1); + } else { + is_odd = false; + } + + return std::make_tuple(region_sign, station, is_odd, ieta); +} + +void GEMEfficiencyHarvester::doResolution(DQMStore::IBooker& ibooker, + DQMStore::IGetter& igetter, + const std::string prefix) { + const std::string resolution_folder = folder_ + "/Resolution/"; + + igetter.setCurrentFolder(resolution_folder); + ibooker.setCurrentFolder(resolution_folder); + + std::map, std::vector > > res_data; + + for (const std::string& name : igetter.getMEs()) { + if (name.find(prefix) == std::string::npos) + continue; + + const std::string fullpath = resolution_folder + name; + const MonitorElement* me = igetter.get(fullpath); + if (me == nullptr) { + edm::LogError(log_category_) << "failed to get " << fullpath << std::endl; + continue; + } + + TH1F* hist = me->getTH1F(); + if (hist == nullptr) { + edm::LogError(log_category_) << "failed to get TH1F" << std::endl; + continue; + } + + auto&& [region_sign, station, is_odd, ieta] = parseResidualName(name, prefix); + if (region_sign.empty() or station < 0 or ieta < 0) { + edm::LogError(log_category_) << "failed to parse the name of the residual histogram: " << name << std::endl; + continue; + } + + const std::tuple key{region_sign, station, is_odd}; + + if (res_data.find(key) == res_data.end()) { + res_data.insert({key, std::vector >()}); + } + res_data[key].emplace_back(ieta, hist); + } // MonitorElement + + ////////////////////////////////////////////////////////////////////////////// + // NOTE + ////////////////////////////////////////////////////////////////////////////// + for (auto [key, ieta_data] : res_data) { + if (ieta_data.empty()) { + continue; + } + + TString tmp_title{ieta_data.front().second->GetTitle()}; + const TObjArray* tokens = tmp_title.Tokenize(":"); + TString title = dynamic_cast(tokens->At(0))->GetString(); + + auto&& [region_sign, station, is_odd] = key; + TString&& name = TString::Format("%s_ge%s%d1", prefix.data(), region_sign.c_str(), station); + title += TString::Format("GE %s%d/1", region_sign.c_str(), station); + if (station == 1) { + name += (is_odd ? "_odd" : "_even"); + title += (is_odd ? ", Odd Superchambers" : ", Even Superchambers"); + } + + const int num_etas = ieta_data.size(); + + TH2F* profile = new TH2F(name, title, num_etas, 0.5, num_etas + 0.5, 2, -0.5, 1.5); + auto x_axis = profile->GetXaxis(); + + x_axis->SetTitle("i#eta"); + for (int ieta = 1; ieta <= num_etas; ieta++) { + const std::string&& label = std::to_string(ieta); + x_axis->SetBinLabel(ieta, label.c_str()); + } + + profile->GetYaxis()->SetBinLabel(1, "Mean"); + profile->GetYaxis()->SetBinLabel(2, "Std. Dev."); + + for (auto [ieta, hist] : ieta_data) { + profile->SetBinContent(ieta, 1, hist->GetMean()); + profile->SetBinContent(ieta, 2, hist->GetStdDev()); + + profile->SetBinError(ieta, 1, hist->GetMeanError()); + profile->SetBinError(ieta, 2, hist->GetStdDevError()); + } + + ibooker.book2D(name, profile); + } +} + +void GEMEfficiencyHarvester::dqmEndJob(DQMStore::IBooker& ibooker, DQMStore::IGetter& igetter) { + doEfficiency(ibooker, igetter); + doResolution(ibooker, igetter, "residual_phi"); +} diff --git a/DQMOffline/Muon/src/GEMOfflineDQMBase.cc b/DQMOffline/Muon/src/GEMOfflineDQMBase.cc new file mode 100644 index 0000000000000..c76249848e123 --- /dev/null +++ b/DQMOffline/Muon/src/GEMOfflineDQMBase.cc @@ -0,0 +1,91 @@ +#include "DQMOffline/Muon/interface/GEMOfflineDQMBase.h" + +GEMOfflineDQMBase::GEMOfflineDQMBase(const edm::ParameterSet& pset) { + log_category_ = pset.getUntrackedParameter("logCategory"); +} + +int GEMOfflineDQMBase::getDetOccXBin(const GEMDetId& gem_id, const edm::ESHandle& gem) { + const GEMSuperChamber* superchamber = gem->superChamber(gem_id); + if (superchamber == nullptr) { + return -1; + } + return getDetOccXBin(gem_id.chamber(), gem_id.layer(), superchamber->nChambers()); +} + +void GEMOfflineDQMBase::setDetLabelsVFAT(MonitorElement* me, const GEMStation* station) { + if (me == nullptr) { + edm::LogError(log_category_) << "MonitorElement* is nullptr" << std::endl; + return; + } + + me->setAxisTitle("Superchamber / Chamber", 1); + for (const GEMSuperChamber* superchamber : station->superChambers()) { + const int num_chambers = superchamber->nChambers(); + for (const GEMChamber* chamber : superchamber->chambers()) { + const int sc = chamber->id().chamber(); + const int ch = chamber->id().layer(); + const int xbin = getDetOccXBin(sc, ch, num_chambers); + const char* label = Form("%d/%d", sc, ch); + me->setBinLabel(xbin, label, 1); + } + } + + me->setAxisTitle("VFAT (i#eta)", 2); + const int max_vfat = getMaxVFAT(station->station()); + if (max_vfat < 0) { + edm::LogError(log_category_) << "Wrong max VFAT: " << max_vfat << " at Station " << station->station() << std::endl; + return; + } + + const int num_etas = getNumEtaPartitions(station); + for (int ieta = 1; ieta <= num_etas; ieta++) { + for (int vfat_phi = 1; vfat_phi <= max_vfat; vfat_phi++) { + const int ybin = getVFATNumber(station->station(), ieta, vfat_phi); + const char* label = Form("%d (%d)", ybin, ieta); + me->setBinLabel(ybin, label, 2); + } + } +} + +void GEMOfflineDQMBase::setDetLabelsEta(MonitorElement* me, const GEMStation* station) { + if (me == nullptr) { + edm::LogError(log_category_) << "MonitorElement* is nullptr" << std::endl; + return; + } + + me->setAxisTitle("Superchamber / Chamber", 1); + for (const GEMSuperChamber* superchamber : station->superChambers()) { + const int num_chambers = superchamber->nChambers(); + + for (const GEMChamber* chamber : superchamber->chambers()) { + const int sc = chamber->id().chamber(); + const int ch = chamber->id().layer(); + const int xbin = getDetOccXBin(sc, ch, num_chambers); + const char* label = Form("%d/%d", sc, ch); + me->setBinLabel(xbin, label, 1); + } + } + + const int num_etas = getNumEtaPartitions(station); + me->setAxisTitle("i#eta", 2); + for (int ieta = 1; ieta <= num_etas; ieta++) { + const std::string&& label = std::to_string(ieta); + me->setBinLabel(ieta, label, 2); + } +} + +int GEMOfflineDQMBase::getNumEtaPartitions(const GEMStation* station) { + const auto&& superchambers = station->superChambers(); + if (not checkRefs(superchambers)) { + edm::LogError(log_category_) << "failed to get a valid vector of GEMSuperChamber ptrs" << std::endl; + return 0; + } + + const auto& chambers = superchambers.front()->chambers(); + if (not checkRefs(chambers)) { + edm::LogError(log_category_) << "failed to get a valid vector of GEMChamber ptrs" << std::endl; + return 0; + } + + return chambers.front()->nEtaPartitions(); +} diff --git a/DQMOffline/Muon/src/GEMOfflineMonitor.cc b/DQMOffline/Muon/src/GEMOfflineMonitor.cc new file mode 100644 index 0000000000000..003ec3cd770e2 --- /dev/null +++ b/DQMOffline/Muon/src/GEMOfflineMonitor.cc @@ -0,0 +1,126 @@ +#include "DQMOffline/Muon/interface/GEMOfflineMonitor.h" + +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "Geometry/CommonTopologies/interface/StripTopology.h" +#include "Geometry/Records/interface/MuonGeometryRecord.h" +#include "DataFormats/Math/interface/deltaPhi.h" + +GEMOfflineMonitor::GEMOfflineMonitor(const edm::ParameterSet& pset) : GEMOfflineDQMBase(pset) { + digi_token_ = consumes(pset.getParameter("digiTag")); + rechit_token_ = consumes(pset.getParameter("recHitTag")); +} + +GEMOfflineMonitor::~GEMOfflineMonitor() {} + +void GEMOfflineMonitor::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription desc; + desc.add("digiTag", edm::InputTag("muonGEMDigis")); + desc.add("recHitTag", edm::InputTag("gemRecHits")); + desc.addUntracked("logCategory", "GEMOfflineMonitor"); + descriptions.add("gemOfflineMonitor", desc); +} + +void GEMOfflineMonitor::bookHistograms(DQMStore::IBooker& ibooker, edm::Run const& run, edm::EventSetup const& isetup) { + edm::ESHandle gem; + isetup.get().get(gem); + if (not gem.isValid()) { + edm::LogError(log_category_) << "GEMGeometry is invalid" << std::endl; + return; + } + + for (const GEMRegion* region : gem->regions()) { + const int region_number = region->region(); + const char* region_sign = region_number > 0 ? "+" : "-"; + + for (const GEMStation* station : region->stations()) { + const int station_number = station->station(); + + const MEMapKey1 det_key{region_number, station_number}; + const auto&& station_name = TString::Format("_ge%s%d1", region_sign, station_number); + const auto&& station_title = TString::Format(" : GE %s%d/1", region_sign, station_number); + bookDetectorOccupancy(ibooker, station, det_key, station_name, station_title); + } // station + } // region +} + +void GEMOfflineMonitor::bookDetectorOccupancy(DQMStore::IBooker& ibooker, + const GEMStation* station, + const MEMapKey1& key, + const TString& name_suffix, + const TString& title_suffix) { + BookingHelper helper(ibooker, name_suffix, title_suffix); + const auto&& superchambers = station->superChambers(); + if (not checkRefs(superchambers)) { + edm::LogError(log_category_) << "failed to get a valid vector of GEMSuperChamber ptrs" << std::endl; + return; + } + + // per station + const int num_superchambers = superchambers.size(); + const int num_chambers = num_superchambers * superchambers.front()->nChambers(); + // the numer of VFATs per GEMEtaPartition + const int max_vfat = getMaxVFAT(station->station()); + // the number of eta partitions per GEMChamber + const int num_etas = getNumEtaPartitions(station); + // the number of VFATs per GEMChamber + const int num_vfat = num_etas * max_vfat; + + // NOTE Digi + ibooker.setCurrentFolder("GEM/GEMOfflineMonitor/Digi"); + me_digi_det_[key] = + helper.book2D("digi_det", "Digi Occupancy", num_chambers, 0.5, num_chambers + 0.5, num_vfat, 0.5, num_vfat + 0.5); + setDetLabelsVFAT(me_digi_det_[key], station); + + // NOTE RecHit + ibooker.setCurrentFolder("GEM/GEMOfflineMonitor/RecHit"); + me_hit_det_[key] = + helper.book2D("hit_det", "Hit Occupancy", num_chambers, 0.5, num_chambers + 0.5, num_etas, 0.5, num_etas + 0.5); + setDetLabelsEta(me_hit_det_[key], station); +} + +void GEMOfflineMonitor::analyze(const edm::Event& event, const edm::EventSetup& setup) { + edm::Handle digi_collection; + event.getByToken(digi_token_, digi_collection); + if (not digi_collection.isValid()) { + edm::LogError(log_category_) << "GEMDigiCollection is invalid!" << std::endl; + return; + } + + edm::Handle rechit_collection; + event.getByToken(rechit_token_, rechit_collection); + if (not rechit_collection.isValid()) { + edm::LogError(log_category_) << "GEMRecHitCollection is invalid" << std::endl; + return; + } + + edm::ESHandle gem; + setup.get().get(gem); + if (not gem.isValid()) { + edm::LogError(log_category_) << "GEMGeometry is invalid" << std::endl; + return; + } + + // GEMDigi + for (auto range_iter = digi_collection->begin(); range_iter != digi_collection->end(); range_iter++) { + const GEMDetId& gem_id = (*range_iter).first; + const GEMDigiCollection::Range& range = (*range_iter).second; + + const MEMapKey1 det_key{gem_id.region(), gem_id.station()}; + for (auto digi = range.first; digi != range.second; ++digi) { + const int chamber_bin = getDetOccXBin(gem_id, gem); + const int vfat_number = getVFATNumberByStrip(gem_id.station(), gem_id.roll(), digi->strip()); + + fillME(me_digi_det_, det_key, chamber_bin, vfat_number); + } + } + + // GEMRecHit + for (auto hit = rechit_collection->begin(); hit != rechit_collection->end(); hit++) { + const GEMDetId&& gem_id = hit->gemId(); + const MEMapKey1 det_key{gem_id.region(), gem_id.station()}; + + const int chamber_bin = getDetOccXBin(gem_id, gem); + fillME(me_hit_det_, det_key, chamber_bin, gem_id.roll()); + } +} diff --git a/DQMOffline/Muon/src/SealModule.cc b/DQMOffline/Muon/src/SealModule.cc index 1a23411d23b42..53eddf5377dc2 100644 --- a/DQMOffline/Muon/src/SealModule.cc +++ b/DQMOffline/Muon/src/SealModule.cc @@ -21,6 +21,9 @@ #include "DQMOffline/Muon/interface/TriggerMatchMonitor.h" #include "DQMOffline/Muon/interface/TriggerMatchEfficiencyPlotter.h" +#include "DQMOffline/Muon/interface/GEMOfflineMonitor.h" +#include "DQMOffline/Muon/interface/GEMEfficiencyAnalyzer.h" +#include "DQMOffline/Muon/interface/GEMEfficiencyHarvester.h" DEFINE_FWK_MODULE(MuonTrackResidualsTest); DEFINE_FWK_MODULE(MuonRecoTest); @@ -39,3 +42,6 @@ DEFINE_FWK_MODULE(MuonSeedsAnalyzer); DEFINE_FWK_MODULE(MuonMiniAOD); DEFINE_FWK_MODULE(TriggerMatchMonitor); DEFINE_FWK_MODULE(TriggerMatchEfficiencyPlotter); +DEFINE_FWK_MODULE(GEMOfflineMonitor); +DEFINE_FWK_MODULE(GEMEfficiencyAnalyzer); +DEFINE_FWK_MODULE(GEMEfficiencyHarvester);