Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-11-26 02:34:27

0001 
0002 #include "RecoEcal/EgammaClusterAlgos/interface/Multi5x5ClusterAlgo.h"
0003 
0004 // Geometry
0005 #include "Geometry/Records/interface/IdealGeometryRecord.h"
0006 #include "Geometry/CaloGeometry/interface/CaloSubdetectorGeometry.h"
0007 #include "Geometry/CaloGeometry/interface/CaloCellGeometry.h"
0008 #include "Geometry/CaloTopology/interface/EcalEndcapTopology.h"
0009 #include "Geometry/CaloTopology/interface/EcalBarrelTopology.h"
0010 #include "RecoEcal/EgammaCoreTools/interface/ClusterEtLess.h"
0011 #include "DataFormats/CaloRecHit/interface/CaloID.h"
0012 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0013 
0014 //temporary sorter, I'm sure this must exist already in CMSSW
0015 template <class T1, class T2, typename Comp = std::less<T1> >
0016 struct PairSortByFirst {
0017   Comp comp;
0018   bool operator()(const std::pair<T1, T2>& lhs, const std::pair<T1, T2>& rhs) { return comp(lhs.first, rhs.first); }
0019   bool operator()(const T1& lhs, const std::pair<T1, T2>& rhs) { return comp(lhs, rhs.first); }
0020   bool operator()(const std::pair<T1, T2>& lhs, const T1& rhs) { return comp(lhs.first, rhs); }
0021   bool operator()(const T1& lhs, const T1& rhs) { return comp(lhs, rhs); }
0022 };
0023 
0024 struct EBDetIdSorter {
0025   bool operator()(const EBDetId& lhs, const EBDetId& rhs) {
0026     if (lhs.ieta() < rhs.ieta())
0027       return true;
0028     else if (lhs.ieta() > rhs.ieta())
0029       return false;
0030     else
0031       return lhs.iphi() < rhs.iphi();
0032   }
0033 };
0034 
0035 struct EEDetIdSorter {
0036   bool operator()(const EEDetId& lhs, const EEDetId& rhs) {
0037     if (lhs.zside() < rhs.zside())
0038       return true;
0039     else if (lhs.zside() > rhs.zside())
0040       return false;
0041     else {  //z is equal, onto ix
0042       if (lhs.ix() < rhs.ix())
0043         return true;
0044       else if (lhs.ix() > rhs.ix())
0045         return false;
0046       else
0047         return lhs.iy() < rhs.iy();
0048     }
0049   }
0050 };
0051 
0052 // Return a vector of clusters from a collection of EcalRecHits:
0053 //
0054 std::vector<reco::BasicCluster> Multi5x5ClusterAlgo::makeClusters(const EcalRecHitCollection* hits,
0055                                                                   const CaloSubdetectorGeometry* geometry_p,
0056                                                                   const CaloSubdetectorTopology* topology_p,
0057                                                                   const CaloSubdetectorGeometry* geometryES_p,
0058                                                                   reco::CaloID::Detectors detector,
0059                                                                   bool regional,
0060                                                                   const std::vector<RectangularEtaPhiRegion>& regions) {
0061   std::vector<EcalRecHit> seeds;
0062 
0063   double threshold = 0;
0064   std::string ecalPart_string;
0065   detector_ = reco::CaloID::DET_NONE;
0066   if (detector == reco::CaloID::DET_ECAL_ENDCAP) {
0067     detector_ = reco::CaloID::DET_ECAL_ENDCAP;
0068     threshold = ecalEndcapSeedThreshold_;
0069     ecalPart_string = "EndCap";
0070   }
0071   if (detector == reco::CaloID::DET_ECAL_BARREL) {
0072     detector_ = reco::CaloID::DET_ECAL_BARREL;
0073     threshold = ecalBarrelSeedThreshold_;
0074     ecalPart_string = "Barrel";
0075   }
0076 
0077   LogTrace("EcalClusters") << "-------------------------------------------------------------";
0078   LogTrace("EcalClusters") << "Island algorithm invoked for ECAL" << ecalPart_string;
0079   LogTrace("EcalClusters") << "Looking for seeds, energy threshold used = " << threshold << " GeV";
0080 
0081   int nregions = 0;
0082   if (regional)
0083     nregions = regions.size();
0084 
0085   if (!regional || nregions) {
0086     for (auto const& hit : *hits) {
0087       double energy = hit.energy();
0088       if (energy < threshold)
0089         continue;  // need to check to see if this line is useful!
0090 
0091       auto thisCell = geometry_p->getGeometry(hit.id());
0092       // Require that RecHit is within clustering region in case
0093       // of regional reconstruction
0094       bool withinRegion = false;
0095       if (regional) {
0096         for (auto const& region : regions) {
0097           if (region.inRegion(thisCell->etaPos(), thisCell->phiPos())) {
0098             withinRegion = true;
0099             break;
0100           }
0101         }
0102       }
0103 
0104       if (!regional || withinRegion) {
0105         float ET = hit.energy() * thisCell->getPosition().basicVector().unit().perp();
0106         if (ET > threshold)
0107           seeds.push_back(hit);
0108       }
0109     }
0110   }
0111 
0112   sort(seeds.begin(), seeds.end(), [](auto const& x, auto const& y) { return (x.energy() > y.energy()); });
0113 
0114   LogTrace("EcalClusters") << "Total number of seeds found in event = " << seeds.size();
0115 
0116   auto clusters_v = mainSearch(hits, geometry_p, topology_p, geometryES_p, seeds);
0117   sort(clusters_v.rbegin(), clusters_v.rend(), isClusterEtLess);
0118 
0119   LogTrace("EcalClusters") << "---------- end of main search. clusters have been sorted ----";
0120 
0121   return clusters_v;
0122 }
0123 
0124 // Search for clusters
0125 //
0126 namespace {
0127   class ProtoClusters {
0128     using ProtoBasicCluster = Multi5x5ClusterAlgo::ProtoBasicCluster;
0129     std::vector<ProtoBasicCluster> clusters_;
0130     std::vector<std::pair<DetId, int> > whichClusCrysBelongsTo_;
0131     bool reassignSeedCrysToClusterItSeeds_;
0132 
0133   public:
0134     ProtoClusters(bool reassignSeedCrysToClusterItSeeds)
0135         : reassignSeedCrysToClusterItSeeds_(reassignSeedCrysToClusterItSeeds) {}
0136     void push_back(ProtoBasicCluster&& c) {
0137       clusters_.push_back(std::move(c));
0138       if (reassignSeedCrysToClusterItSeeds_) {
0139         for (auto const& hit : clusters_.back().hits()) {
0140           whichClusCrysBelongsTo_.push_back(std::pair<DetId, int>(hit.first, clusters_.size() - 1));
0141         }
0142       }
0143     }
0144     std::vector<ProtoBasicCluster> finalize() {
0145       if (reassignSeedCrysToClusterItSeeds_) {
0146         std::sort(whichClusCrysBelongsTo_.begin(), whichClusCrysBelongsTo_.end(), PairSortByFirst<DetId, int>());
0147 
0148         for (size_t clusNr = 0; clusNr < clusters_.size(); clusNr++) {
0149           if (!clusters_[clusNr].containsSeed()) {
0150             const EcalRecHit& seedHit = clusters_[clusNr].seed();
0151             typedef std::vector<std::pair<DetId, int> >::iterator It;
0152             std::pair<It, It> result = std::equal_range(whichClusCrysBelongsTo_.begin(),
0153                                                         whichClusCrysBelongsTo_.end(),
0154                                                         seedHit.id(),
0155                                                         PairSortByFirst<DetId, int>());
0156 
0157             if (result.first != result.second)
0158               clusters_[result.first->second].removeHit(seedHit);
0159             clusters_[clusNr].addSeed();
0160           }
0161         }
0162       }
0163       whichClusCrysBelongsTo_.clear();
0164       return std::move(clusters_);
0165     }
0166   };
0167 }  // namespace
0168 
0169 std::vector<reco::BasicCluster> Multi5x5ClusterAlgo::mainSearch(const EcalRecHitCollection* hits,
0170                                                                 const CaloSubdetectorGeometry* geometry_p,
0171                                                                 const CaloSubdetectorTopology* topology_p,
0172                                                                 const CaloSubdetectorGeometry* geometryES_p,
0173                                                                 std::vector<EcalRecHit> const& seeds) {
0174   LogTrace("EcalClusters") << "Building clusters............";
0175 
0176   // The vector of clusters
0177   std::vector<reco::BasicCluster> clusters_v;
0178 
0179   // The set of used DetID's
0180   std::set<DetId> used_s;
0181   // set of crystals not to be added but which can seed
0182   // a new 3x3 (e.g. the outer crystals in a 5x5)
0183   std::set<DetId> canSeed_s;
0184 
0185   ProtoClusters protoClusters{reassignSeedCrysToClusterItSeeds_};
0186 
0187   // Loop over seeds:
0188   bool first = true;
0189   for (auto const& seed : seeds) {
0190     struct Guard {
0191       bool& b_;
0192       Guard(bool& b) : b_{b} {};
0193       ~Guard() { b_ = false; }
0194     };
0195     Guard guard(first);
0196     // check if this crystal is able to seed
0197     // (event though it is already used)
0198     bool usedButCanSeed = false;
0199     if (canSeed_s.find(seed.id()) != canSeed_s.end())
0200       usedButCanSeed = true;
0201 
0202     // avoid seeding for anomalous channels
0203     if (!seed.checkFlag(EcalRecHit::kGood)) {  // if rechit is good, no need for further checks
0204       if (seed.checkFlags(v_chstatus_))
0205         continue;
0206     }
0207 
0208     // make sure the current seed does not belong to a cluster already.
0209     if ((used_s.find(seed.id()) != used_s.end()) && (usedButCanSeed == false)) {
0210       if (first) {
0211         LogTrace("EcalClusters") << "##############################################################";
0212         LogTrace("EcalClusters") << "DEBUG ALERT: Highest energy seed already belongs to a cluster!";
0213         LogTrace("EcalClusters") << "##############################################################";
0214       }
0215 
0216       // seed crystal is used or is used and cannot seed a cluster
0217       // so continue to the next seed crystal...
0218       continue;
0219     }
0220 
0221     // Create a navigator at the seed and get seed
0222     // energy
0223     CaloNavigator<DetId> navigator(seed.id(), topology_p);
0224     DetId seedId = navigator.pos();
0225     EcalRecHitCollection::const_iterator seedIt = hits->find(seedId);
0226     navigator.setHome(seedId);
0227 
0228     // Is the seed a local maximum?
0229     bool localMaxima = checkMaxima(navigator, hits);
0230 
0231     if (localMaxima) {
0232       // build the 5x5 taking care over which crystals
0233       // can seed new clusters and which can't
0234       auto current_v = prepareCluster(navigator, hits, geometry_p, used_s, canSeed_s);
0235 
0236       // If some crystals in the current vector then
0237       // make them into a cluster
0238       if (!current_v.empty()) {
0239         auto c = makeCluster(hits, geometry_p, geometryES_p, seedIt, usedButCanSeed, current_v);
0240         if (c) {
0241           protoClusters.push_back(std::move(*c));
0242         } else {
0243           for (auto const& current : current_v) {
0244             used_s.erase(current.first);
0245           }
0246         }
0247       }
0248     }
0249   }  // End loop on seed crystals
0250 
0251   auto finalProtoClusters = protoClusters.finalize();
0252 
0253   for (auto const& protoCluster : finalProtoClusters) {
0254     Point position = posCalculator_.Calculate_Location(protoCluster.hits(), hits, geometry_p, geometryES_p);
0255     clusters_v.emplace_back(protoCluster.energy(),
0256                             position,
0257                             reco::CaloID(detector_),
0258                             protoCluster.hits(),
0259                             reco::CaloCluster::multi5x5,
0260                             protoCluster.seed().id());
0261   }
0262 
0263   return clusters_v;
0264 }
0265 
0266 std::optional<Multi5x5ClusterAlgo::ProtoBasicCluster> Multi5x5ClusterAlgo::makeCluster(
0267     const EcalRecHitCollection* hits,
0268     const CaloSubdetectorGeometry* geometry,
0269     const CaloSubdetectorGeometry* geometryES,
0270     const EcalRecHitCollection::const_iterator& seedIt,
0271     bool seedOutside,
0272     std::vector<std::pair<DetId, float> >& current_v) {
0273   double energy = 0;
0274   //double chi2   = 0;
0275   reco::CaloID caloID;
0276   Point position;
0277   position = posCalculator_.Calculate_Location(current_v, hits, geometry, geometryES);
0278 
0279   for (auto const& hitInfo : current_v) {
0280     EcalRecHitCollection::const_iterator itt = hits->find(hitInfo.first);
0281     EcalRecHit hit_p = *itt;
0282     energy += hit_p.energy();
0283     //chi2 += 0;
0284     if (hitInfo.first.subdetId() == EcalBarrel) {
0285       caloID = reco::CaloID::DET_ECAL_BARREL;
0286     } else {
0287       caloID = reco::CaloID::DET_ECAL_ENDCAP;
0288     }
0289   }
0290   //chi2 /= energy;
0291 
0292   LogTrace("EcalClusters") << "******** NEW CLUSTER ********";
0293   LogTrace("EcalClusters") << "No. of crystals = " << current_v.size();
0294   LogTrace("EcalClusters") << "     Energy     = " << energy;
0295   LogTrace("EcalClusters") << "     Phi        = " << position.phi();
0296   LogTrace("EcalClusters") << "     Eta " << position.eta();
0297   LogTrace("EcalClusters") << "*****************************";
0298 
0299   // to be a valid cluster the cluster energy
0300   // must be at least the seed energy
0301   double seedEnergy = seedIt->energy();
0302   if ((seedOutside && energy >= 0) || (!seedOutside && energy >= seedEnergy)) {
0303     return ProtoBasicCluster(energy, *seedIt, std::move(current_v));
0304 
0305     // clusters_v.push_back(reco::BasicCluster(energy, position, reco::CaloID(detector_), current_v, reco::CaloCluster::multi5x5, seedIt->id()));
0306 
0307     // if no valid cluster was built,
0308     // then free up these crystals to be used in the next...
0309   }  //else
0310   return {};
0311 }
0312 
0313 bool Multi5x5ClusterAlgo::checkMaxima(CaloNavigator<DetId>& navigator, const EcalRecHitCollection* hits) const {
0314   bool maxima = true;
0315   EcalRecHitCollection::const_iterator thisHit;
0316   EcalRecHitCollection::const_iterator seedHit = hits->find(navigator.pos());
0317   double seedEnergy = seedHit->energy();
0318 
0319   std::array<DetId, 4> swissCrossVec;
0320 
0321   swissCrossVec[0] = navigator.west();
0322   navigator.home();
0323   swissCrossVec[1] = navigator.east();
0324   navigator.home();
0325   swissCrossVec[2] = navigator.north();
0326   navigator.home();
0327   swissCrossVec[3] = navigator.south();
0328   navigator.home();
0329 
0330   for (auto const& det : swissCrossVec) {
0331     // look for this hit
0332     thisHit = hits->find(det);
0333 
0334     // continue if this hit was not found
0335     if ((det == DetId(0)) || thisHit == hits->end())
0336       continue;
0337 
0338     // the recHit has to be skipped in the local maximum search if it was found
0339     // in the map of channels to be excluded
0340     uint32_t rhFlag = thisHit->recoFlag();
0341     std::vector<int>::const_iterator vit = std::find(v_chstatus_.begin(), v_chstatus_.end(), rhFlag);
0342     if (vit != v_chstatus_.end())
0343       continue;
0344 
0345     // if this crystal has more energy than the seed then we do
0346     // not have a local maxima
0347     if (thisHit->energy() > seedEnergy) {
0348       maxima = false;
0349       break;
0350     }
0351   }
0352 
0353   return maxima;
0354 }
0355 
0356 std::vector<std::pair<DetId, float> > Multi5x5ClusterAlgo::prepareCluster(CaloNavigator<DetId>& navigator,
0357                                                                           const EcalRecHitCollection* hits,
0358                                                                           const CaloSubdetectorGeometry* geometry,
0359                                                                           std::set<DetId>& used_s,
0360                                                                           std::set<DetId>& canSeed_s) const {
0361   std::vector<std::pair<DetId, float> > current_v;
0362   DetId thisDet;
0363   std::set<DetId>::iterator setItr;
0364 
0365   // now add the 5x5 taking care to mark the edges
0366   // as able to seed and where overlapping in the central
0367   // region with crystals that were previously able to seed
0368   // change their status so they are not able to seed
0369   //std::cout << std::endl;
0370   for (int dx = -2; dx < 3; ++dx) {
0371     for (int dy = -2; dy < 3; ++dy) {
0372       // navigate in free steps forming
0373       // a full 5x5
0374       thisDet = navigator.offsetBy(dx, dy);
0375       navigator.home();
0376 
0377       // add the current crystal
0378       //std::cout << "adding " << dx << ", " << dy << std::endl;
0379       if (addCrystal(thisDet, *hits) and (used_s.find(thisDet) == used_s.end())) {
0380         current_v.push_back(std::pair<DetId, float>(thisDet, 1.));  // by default hit energy fractions are set at 1.
0381         used_s.insert(thisDet);
0382       }
0383 
0384       // now consider if we are in an edge (outer 16)
0385       // or central (inner 9) region
0386       if ((abs(dx) > 1) || (abs(dy) > 1)) {
0387         // this is an "edge" so should be allowed to seed
0388         // provided it is not already used
0389         //std::cout << "   setting can seed" << std::endl;
0390         canSeed_s.insert(thisDet);
0391       }  // end if "edge"
0392       else {
0393         // or else we are in the central 3x3
0394         // and must remove any of these crystals from the canSeed set
0395         setItr = canSeed_s.find(thisDet);
0396         if (setItr != canSeed_s.end()) {
0397           //std::cout << "   unsetting can seed" << std::endl;
0398           canSeed_s.erase(setItr);
0399         }
0400       }  // end if "centre"
0401 
0402     }  // end loop on dy
0403 
0404   }  // end loop on dx
0405 
0406   //std::cout << "*** " << std::endl;
0407   //std::cout << " current_v contains " << current_v.size() << std::endl;
0408   //std::cout << "*** " << std::endl;
0409   return current_v;
0410 }
0411 
0412 bool Multi5x5ClusterAlgo::addCrystal(const DetId& det, const EcalRecHitCollection& recHits) {
0413   auto thisIt = recHits.find(det);
0414   return ((thisIt != recHits.end()) && (thisIt->id() != DetId(0)));
0415 }
0416 
0417 bool Multi5x5ClusterAlgo::ProtoBasicCluster::removeHit(const EcalRecHit& hitToRM) {
0418   std::vector<std::pair<DetId, float> >::iterator hitPos;
0419   for (hitPos = hits_.begin(); hitPos < hits_.end(); hitPos++) {
0420     if (hitToRM.id() == hitPos->first)
0421       break;
0422   }
0423   if (hitPos != hits_.end()) {
0424     hits_.erase(hitPos);
0425     energy_ -= hitToRM.energy();
0426     return true;
0427   }
0428   return false;
0429 }
0430 
0431 //now the tricky thing is to make sure we insert it in the right place in the hit vector
0432 //order is from -2,-2, -2,-1 to 2,2 with seed being 0,0 (eta,phi)
0433 bool Multi5x5ClusterAlgo::ProtoBasicCluster::addSeed() {
0434   typedef std::vector<std::pair<DetId, float> >::iterator It;
0435   std::pair<It, It> hitPos;
0436 
0437   if (seed_.id().subdetId() == EcalBarrel) {
0438     hitPos = std::equal_range(hits_.begin(), hits_.end(), seed_.id(), PairSortByFirst<DetId, float, EBDetIdSorter>());
0439   } else {
0440     hitPos = std::equal_range(hits_.begin(), hits_.end(), seed_.id(), PairSortByFirst<DetId, float, EEDetIdSorter>());
0441   }
0442 
0443   if (hitPos.first == hitPos.second) {  //it doesnt already exist in the vec, add it
0444     hits_.insert(hitPos.first, std::pair<DetId, float>(seed_.id(), 1.));
0445     energy_ += seed_.energy();
0446     containsSeed_ = true;
0447 
0448     return true;
0449   } else
0450     return false;
0451 }
0452 
0453 bool Multi5x5ClusterAlgo::ProtoBasicCluster::isSeedCrysInHits_() const {
0454   for (auto const& hit : hits_) {
0455     if (seed_.id() == hit.first)
0456       return true;
0457   }
0458   return false;
0459 }