Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-02-14 14:22:49

0001 /**
0002  *  \class GEMPadDigiClusterProducer
0003  *
0004  *  Produces GEM pad clusters from at most 8 adjacent GEM pads.
0005  *  Clusters are used downstream in the CSC local trigger to build
0006  *  GEM-CSC triggers and in the muon trigger to build EMTF tracks
0007  *
0008  *  Based on documentation provided by the GEM firmware architects
0009  *
0010  *  \author Sven Dildick (TAMU)
0011  */
0012 
0013 #include "FWCore/Framework/interface/MakerMacros.h"
0014 #include "FWCore/PluginManager/interface/ModuleDef.h"
0015 #include "FWCore/Framework/interface/stream/EDProducer.h"
0016 #include "FWCore/Framework/interface/MakerMacros.h"
0017 #include "FWCore/Framework/interface/ESHandle.h"
0018 #include "FWCore/Framework/interface/ConsumesCollector.h"
0019 #include "FWCore/Framework/interface/Event.h"
0020 #include "FWCore/Framework/interface/EventSetup.h"
0021 
0022 #include "FWCore/Utilities/interface/Exception.h"
0023 #include "FWCore/Utilities/interface/ESGetToken.h"
0024 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0025 #include "FWCore/Utilities/interface/InputTag.h"
0026 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0027 
0028 #include "Geometry/Records/interface/MuonGeometryRecord.h"
0029 #include "Geometry/GEMGeometry/interface/GEMGeometry.h"
0030 
0031 #include "DataFormats/Common/interface/Handle.h"
0032 #include "DataFormats/GEMDigi/interface/GEMPadDigiCollection.h"
0033 #include "DataFormats/GEMDigi/interface/GEMPadDigiClusterCollection.h"
0034 
0035 #include <string>
0036 #include <map>
0037 #include <vector>
0038 
0039 class GEMPadDigiClusterProducer : public edm::stream::EDProducer<> {
0040 public:
0041   // all clusters per eta partition
0042   typedef std::vector<GEMPadDigiCluster> GEMPadDigiClusters;
0043   typedef std::map<GEMDetId, GEMPadDigiClusters> GEMPadDigiClusterContainer;
0044 
0045   // all clusters sorted by chamber, by opthohybrid and by eta partition
0046   typedef std::map<GEMDetId, std::vector<std::vector<std::pair<GEMDetId, GEMPadDigiClusters> > > >
0047       GEMPadDigiClusterSortedContainer;
0048 
0049   explicit GEMPadDigiClusterProducer(const edm::ParameterSet& ps);
0050 
0051   ~GEMPadDigiClusterProducer() override;
0052 
0053   void beginRun(const edm::Run&, const edm::EventSetup&) override;
0054 
0055   void produce(edm::Event&, const edm::EventSetup&) override;
0056 
0057   static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
0058 
0059 private:
0060   /**
0061    *
0062    *************************************
0063    ** Light Cluster Packing Algorithm **
0064    *************************************
0065 
0066    Based on: https://github.com/cms-gem-daq-project/OptoHybridv3/raw/master/doc/OH_modules.docx
0067    (Andrew Peck, Thomas Lenzi, Evaldas Juska)
0068 
0069    In the current version of the algorithm, cluster finding is segmented
0070    into two separate halves of the GE1/1 chambers. Thus, each one of the
0071    trigger fibers can transmit clusters only from the half of the chamber
0072    that it corresponds to. For GE2/1, there are four separate quarts of
0073    the GE2/1 chamber.
0074 
0075    This has the downside of being unable to transmit more than 4 clusters
0076    when they occur within that side of the chamber, so there will be a
0077    slightly higher rate of cluster overflow. For GE2/1 each OH can transmit
0078    up to 5 clusters.
0079 
0080    The benefit, however, is in terms of (1) latency and (2) resource usage.
0081 
0082    The burden of finding clusters on  of the chamber is significantly less,
0083    and allows the cluster packer to operate in a simple, pipelined architecture
0084    which returns up to 4 (or 5) clusters per half-chamber per bunch crossing.
0085 
0086    This faster architecture allows the mechanism to operate with only a
0087    single copy of the cluster finding priority encoder and cluster truncator
0088    (instead of two multiplexed copies), so the total resource usage of
0089    these stages is approximately half.
0090 
0091    Further, a second step of cluster merging that is required in the full
0092    algorithm is avoided, which reduces latency by an additional bunch
0093    crossing and significantly reduces resource usage as well.
0094 
0095    The sorting of the clusters favors lower eta partitions and lower pad numbers
0096   */
0097 
0098   void buildClusters(const GEMPadDigiCollection& pads, GEMPadDigiClusterContainer& out_clusters) const;
0099   void sortClusters(const GEMPadDigiClusterContainer& in_clusters,
0100                     GEMPadDigiClusterSortedContainer& out_clusters) const;
0101   void selectClusters(const GEMPadDigiClusterSortedContainer& in, GEMPadDigiClusterCollection& out) const;
0102   template <class T>
0103   void checkValid(const T& cluster, const GEMDetId& id) const;
0104 
0105   /// Name of input digi Collection
0106   edm::EDGetTokenT<GEMPadDigiCollection> pad_token_;
0107   edm::ESGetToken<GEMGeometry, MuonGeometryRecord> geom_token_;
0108   edm::InputTag pads_;
0109 
0110   unsigned int maxClustersOHGE11_;
0111   unsigned int maxClustersOHGE21_;
0112   unsigned int nOHGE11_;
0113   unsigned int nOHGE21_;
0114   unsigned int maxClusterSize_;
0115   bool sendOverflowClusters_;
0116 
0117   const GEMGeometry* geometry_;
0118 };
0119 
0120 GEMPadDigiClusterProducer::GEMPadDigiClusterProducer(const edm::ParameterSet& ps) : geometry_(nullptr) {
0121   pads_ = ps.getParameter<edm::InputTag>("InputCollection");
0122   maxClustersOHGE11_ = ps.getParameter<unsigned int>("maxClustersOHGE11");
0123   maxClustersOHGE21_ = ps.getParameter<unsigned int>("maxClustersOHGE21");
0124   nOHGE11_ = ps.getParameter<unsigned int>("nOHGE11");
0125   nOHGE21_ = ps.getParameter<unsigned int>("nOHGE21");
0126   maxClusterSize_ = ps.getParameter<unsigned int>("maxClusterSize");
0127   sendOverflowClusters_ = ps.getParameter<bool>("sendOverflowClusters");
0128 
0129   if (sendOverflowClusters_) {
0130     maxClustersOHGE11_ *= 2;
0131     maxClustersOHGE21_ *= 2;
0132   }
0133 
0134   pad_token_ = consumes<GEMPadDigiCollection>(pads_);
0135   geom_token_ = esConsumes<GEMGeometry, MuonGeometryRecord, edm::Transition::BeginRun>();
0136 
0137   produces<GEMPadDigiClusterCollection>();
0138   consumes<GEMPadDigiCollection>(pads_);
0139 }
0140 
0141 GEMPadDigiClusterProducer::~GEMPadDigiClusterProducer() {}
0142 
0143 void GEMPadDigiClusterProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
0144   edm::ParameterSetDescription desc;
0145   desc.add<edm::InputTag>("InputCollection", edm::InputTag("simMuonGEMPadDigis"));
0146   desc.add<unsigned int>("maxClustersOHGE11", 4);
0147   desc.add<unsigned int>("maxClustersOHGE21", 5);
0148   desc.add<unsigned int>("nOHGE11", 2);
0149   desc.add<unsigned int>("nOHGE21", 4);
0150   desc.add<unsigned int>("maxClusterSize", 8);
0151   desc.add<bool>("sendOverflowClusters", false);
0152 
0153   descriptions.add("simMuonGEMPadDigiClustersDef", desc);
0154 }
0155 
0156 void GEMPadDigiClusterProducer::beginRun(const edm::Run& run, const edm::EventSetup& eventSetup) {
0157   edm::ESHandle<GEMGeometry> hGeom = eventSetup.getHandle(geom_token_);
0158   geometry_ = &*hGeom;
0159 }
0160 
0161 void GEMPadDigiClusterProducer::produce(edm::Event& e, const edm::EventSetup& eventSetup) {
0162   edm::Handle<GEMPadDigiCollection> hpads;
0163   e.getByToken(pad_token_, hpads);
0164 
0165   // Create empty output
0166   std::unique_ptr<GEMPadDigiClusterCollection> pClusters(new GEMPadDigiClusterCollection());
0167 
0168   // build the proto clusters (per partition)
0169   GEMPadDigiClusterContainer proto_clusters;
0170   buildClusters(*(hpads.product()), proto_clusters);
0171 
0172   // // sort clusters per chamber, per OH, per partition number and per pad number
0173   GEMPadDigiClusterSortedContainer sorted_clusters;
0174   sortClusters(proto_clusters, sorted_clusters);
0175 
0176   // select the clusters from sorted clusters
0177   selectClusters(sorted_clusters, *pClusters);
0178 
0179   // store them in the event
0180   e.put(std::move(pClusters));
0181 }
0182 
0183 void GEMPadDigiClusterProducer::buildClusters(const GEMPadDigiCollection& det_pads,
0184                                               GEMPadDigiClusterContainer& proto_clusters) const {
0185   // clear the container
0186   proto_clusters.clear();
0187 
0188   // construct clusters
0189   for (const auto& part : geometry_->etaPartitions()) {
0190     // clusters are not build for ME0
0191     // -> ignore hits from station 0
0192     if (part->isME0())
0193       continue;
0194 
0195     GEMPadDigiClusters all_pad_clusters;
0196 
0197     auto pads = det_pads.get(part->id());
0198     std::vector<uint16_t> cl;
0199     int startBX = 99;
0200 
0201     for (auto d = pads.first; d != pads.second; ++d) {
0202       // check if the input pad is valid
0203       checkValid(*d, part->id());
0204 
0205       // number of eta partitions
0206       unsigned nPart = d->nPartitions();
0207 
0208       if (cl.empty()) {
0209         cl.push_back((*d).pad());
0210       } else {
0211         if ((*d).bx() == startBX and            // same bunch crossing
0212             (*d).pad() == cl.back() + 1         // pad difference is 1
0213             and cl.size() < maxClusterSize_) {  // max 8 in cluster
0214           cl.push_back((*d).pad());
0215         } else {
0216           // put the current cluster in the proto collection
0217           GEMPadDigiCluster pad_cluster(cl, startBX, part->subsystem(), nPart);
0218 
0219           // check if the output cluster is valid
0220           checkValid(pad_cluster, part->id());
0221 
0222           all_pad_clusters.emplace_back(pad_cluster);
0223 
0224           // start a new cluster
0225           cl.clear();
0226           cl.push_back((*d).pad());
0227         }
0228       }
0229       startBX = (*d).bx();
0230     }
0231 
0232     // put the last cluster in the proto collection
0233     if (pads.first != pads.second) {
0234       // number of eta partitions
0235       unsigned nPart = (pads.first)->nPartitions();
0236 
0237       GEMPadDigiCluster pad_cluster(cl, startBX, part->subsystem(), nPart);
0238 
0239       // check if the output cluster is valid
0240       checkValid(pad_cluster, part->id());
0241 
0242       all_pad_clusters.emplace_back(pad_cluster);
0243     }
0244     proto_clusters.emplace(part->id(), all_pad_clusters);
0245 
0246   }  // end of partition loop
0247 }
0248 
0249 void GEMPadDigiClusterProducer::sortClusters(const GEMPadDigiClusterContainer& proto_clusters,
0250                                              GEMPadDigiClusterSortedContainer& sorted_clusters) const {
0251   // The sorting of the clusters favors lower eta partitions and lower pad numbers
0252   // By default the eta partitions are sorted by Id
0253 
0254   sorted_clusters.clear();
0255 
0256   for (const auto& ch : geometry_->chambers()) {
0257     // check the station number
0258     const unsigned nOH = ch->id().isGE11() ? nOHGE11_ : nOHGE21_;
0259     const unsigned nPartOH = ch->nEtaPartitions() / nOH;
0260 
0261     std::vector<std::vector<std::pair<GEMDetId, GEMPadDigiClusters> > > temp_clustersCH;
0262 
0263     for (unsigned int iOH = 0; iOH < nOH; iOH++) {
0264       // all clusters for a set of eta partitions
0265       std::vector<std::pair<GEMDetId, GEMPadDigiClusters> > temp_clustersOH;
0266 
0267       // loop over the 4 or 2 eta partitions for this optohybrid
0268       for (unsigned iPart = 1; iPart <= nPartOH; iPart++) {
0269         // get the clusters for this eta partition
0270         const GEMDetId& partId = ch->etaPartition(iPart + iOH * nPartOH)->id();
0271         if (proto_clusters.find(partId) != proto_clusters.end()) {
0272           temp_clustersOH.emplace_back(partId, proto_clusters.at(partId));
0273         }
0274       }  // end of eta partition loop
0275 
0276       temp_clustersCH.emplace_back(temp_clustersOH);
0277     }  // end of OH loop
0278 
0279     sorted_clusters.emplace(ch->id(), temp_clustersCH);
0280   }  // end of chamber loop
0281 }
0282 
0283 void GEMPadDigiClusterProducer::selectClusters(const GEMPadDigiClusterSortedContainer& sorted_clusters,
0284                                                GEMPadDigiClusterCollection& out_clusters) const {
0285   // loop over chambers
0286   for (const auto& ch : geometry_->chambers()) {
0287     const unsigned maxClustersOH = ch->id().isGE11() ? maxClustersOHGE11_ : maxClustersOHGE21_;
0288 
0289     // loop over the optohybrids
0290     for (const auto& optohybrid : sorted_clusters.at(ch->id())) {
0291       // at most maxClustersOH per OH!
0292       unsigned nClusters = 0;
0293 
0294       // loop over the eta partitions for this OH
0295       for (const auto& etapart : optohybrid) {
0296         const auto& detid(etapart.first);
0297         const auto& clusters(etapart.second);
0298 
0299         // pick the clusters with lowest pad number
0300         for (const auto& clus : clusters) {
0301           if (nClusters < maxClustersOH) {
0302             // check if the output cluster is valid
0303             checkValid(clus, detid);
0304 
0305             out_clusters.insertDigi(detid, clus);
0306             nClusters++;
0307           }
0308         }  // end of cluster loop
0309       }    // end of eta partition loop
0310     }      // end of OH loop
0311   }        // end of chamber loop
0312 }
0313 
0314 template <class T>
0315 void GEMPadDigiClusterProducer::checkValid(const T& tp, const GEMDetId& id) const {
0316   // check if the pad/cluster is valid
0317   // in principle, invalid pads/clusters can appear in the CMS raw data
0318   if (!tp.isValid()) {
0319     edm::LogWarning("GEMPadDigiClusterProducer") << "Invalid " << tp << " in " << id;
0320   }
0321 }
0322 
0323 DEFINE_FWK_MODULE(GEMPadDigiClusterProducer);