CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_7/src/RecoEcal/EgammaClusterAlgos/src/PFECALSuperClusterAlgo.cc

Go to the documentation of this file.
00001 #include "RecoEcal/EgammaClusterAlgos/interface/PFECALSuperClusterAlgo.h"
00002 #include "RecoParticleFlow/PFClusterTools/interface/PFClusterWidthAlgo.h"
00003 #include "RecoParticleFlow/PFClusterTools/interface/LinkByRecHit.h"
00004 #include "DataFormats/ParticleFlowReco/interface/PFLayer.h"
00005 #include "DataFormats/HcalDetId/interface/HcalDetId.h"
00006 #include "RecoEcal/EgammaCoreTools/interface/Mustache.h"
00007 #include "Math/GenVector/VectorUtil.h"
00008 #include "TFile.h"
00009 #include "TH2F.h"
00010 #include "TROOT.h"
00011 #include "TMath.h"
00012 
00013 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00014 
00015 #include <stdexcept>
00016 #include <string>
00017 #include <sstream>
00018 #include <cmath>
00019 
00020 using namespace std;
00021 namespace MK = reco::MustacheKernel;
00022 
00023 
00024 namespace {
00025   typedef edm::View<reco::PFCluster> PFClusterView;
00026   typedef edm::Ptr<reco::PFCluster> PFClusterPtr;
00027   typedef edm::PtrVector<reco::PFCluster> PFClusterPtrVector;
00028   typedef PFECALSuperClusterAlgo::CalibratedClusterPtr CalibClusterPtr;
00029   typedef PFECALSuperClusterAlgo::CalibratedClusterPtrVector CalibClusterPtrVector;
00030 
00031   typedef std::binary_function<const CalibClusterPtr&,
00032                                const CalibClusterPtr&, 
00033                                bool> ClusBinaryFunction;
00034   
00035   typedef std::unary_function<const CalibClusterPtr&, 
00036                               bool> ClusUnaryFunction;  
00037 
00038   struct SumPSEnergy : public std::binary_function<double,
00039                                                    const PFClusterPtr&,
00040                                                    double> {
00041     PFLayer::Layer _thelayer;
00042     SumPSEnergy(PFLayer::Layer layer) : _thelayer(layer) {}
00043     double operator()(double a,
00044                       const PFClusterPtr& b) {
00045       return a + (_thelayer == b->layer())*b->energy();
00046     }
00047   };
00048 
00049   struct GreaterByE : public ClusBinaryFunction {
00050     bool operator()(const CalibClusterPtr& x, 
00051                     const CalibClusterPtr& y) { 
00052       return x->energy() > y->energy() ; 
00053     }
00054   };
00055 
00056   struct GreaterByEt : public ClusBinaryFunction {
00057     bool operator()(const CalibClusterPtr& x, 
00058                     const CalibClusterPtr& y) { 
00059       return x->energy()/std::cosh(x->eta()) > y->energy()/std::cosh(y->eta());
00060     }
00061   };
00062 
00063   struct IsASeed : public ClusUnaryFunction {
00064     double threshold;
00065     IsASeed(double thresh) : threshold(thresh) {}
00066     bool operator()(const CalibClusterPtr& x) { 
00067       return x->energy() > threshold; 
00068     }
00069   };
00070 
00071   struct IsLinkedByRecHit : public ClusUnaryFunction {
00072     const CalibClusterPtr the_seed;
00073     const double _threshold, _majority;
00074     const double _maxSatelliteDEta, _maxSatelliteDPhi;
00075     double x_rechits_tot, x_rechits_match;
00076     IsLinkedByRecHit(const CalibClusterPtr& s, const double threshold,
00077                      const double majority, const double maxDEta,
00078                      const double maxDPhi) : 
00079       the_seed(s),_threshold(threshold),_majority(majority), 
00080       _maxSatelliteDEta(maxDEta), _maxSatelliteDPhi(maxDPhi) {}
00081     bool operator()(const CalibClusterPtr& x) {      
00082       if( the_seed->energy_nocalib() < _threshold ) return false; 
00083       const double dEta = std::abs(the_seed->eta()-x->eta());
00084       const double dPhi = 
00085         std::abs(TVector2::Phi_mpi_pi(the_seed->phi() - x->phi())); 
00086       if( _maxSatelliteDEta < dEta || _maxSatelliteDPhi < dPhi) return false;
00087       // now see if the clusters overlap in rechits
00088       const auto& seedHitsAndFractions = 
00089         the_seed->the_ptr()->hitsAndFractions();
00090       const auto& xHitsAndFractions = 
00091         x->the_ptr()->hitsAndFractions();      
00092       x_rechits_tot   = xHitsAndFractions.size();
00093       x_rechits_match = 0.0;      
00094       for( const std::pair<DetId, float>& seedHit : seedHitsAndFractions ) {
00095         for( const std::pair<DetId, float>& xHit : xHitsAndFractions ) {
00096           if( seedHit.first == xHit.first ) {       
00097             x_rechits_match += 1.0;
00098           }
00099         }       
00100       }      
00101       return x_rechits_match/x_rechits_tot > _majority;
00102     }
00103   };
00104 
00105   struct IsClustered : public ClusUnaryFunction {
00106     const CalibClusterPtr the_seed;    
00107     PFECALSuperClusterAlgo::clustering_type _type;
00108     bool dynamic_dphi;
00109     double etawidthSuperCluster_, phiwidthSuperCluster_;
00110     IsClustered(const CalibClusterPtr s, 
00111                 PFECALSuperClusterAlgo::clustering_type ct,
00112                 const bool dyn_dphi) : 
00113       the_seed(s), _type(ct), dynamic_dphi(dyn_dphi) {}
00114     bool operator()(const CalibClusterPtr& x) { 
00115       const double dphi = 
00116         std::abs(TVector2::Phi_mpi_pi(the_seed->phi() - x->phi()));  
00117       const bool isEB = ( PFLayer::ECAL_BARREL == x->the_ptr()->layer() );
00118       const bool passes_dphi = 
00119         ( (!dynamic_dphi && dphi < phiwidthSuperCluster_ ) || 
00120           (dynamic_dphi && MK::inDynamicDPhiWindow(isEB,the_seed->phi(),
00121                                                    x->energy_nocalib(),
00122                                                    x->eta(),
00123                                                    x->phi()) ) );
00124 
00125       switch( _type ) {
00126       case PFECALSuperClusterAlgo::kBOX:
00127         return ( std::abs(the_seed->eta()-x->eta())<etawidthSuperCluster_ && 
00128                  passes_dphi   );
00129         break;
00130       case PFECALSuperClusterAlgo::kMustache:
00131         return ( passes_dphi && 
00132                  MK::inMustache(the_seed->eta(), 
00133                                 the_seed->phi(),
00134                                 x->energy_nocalib(),
00135                                 x->eta(),
00136                                 x->phi()            ));
00137         break;
00138       default: 
00139         return false;
00140       }
00141       return false;
00142     }
00143   };
00144 
00145   double testPreshowerDistance(const edm::Ptr<reco::PFCluster>& eeclus,
00146                                const edm::Ptr<reco::PFCluster>& psclus) {
00147     if( psclus.isNull() || eeclus.isNull() ) return -1.0;
00148     /* 
00149     // commented out since PFCluster::layer() uses a lot of CPU
00150     // and since 
00151     if( PFLayer::ECAL_ENDCAP != eeclus->layer() ) return -1.0;
00152     if( PFLayer::PS1 != psclus->layer() &&
00153         PFLayer::PS2 != psclus->layer()    ) {
00154       throw cms::Exception("testPreshowerDistance")
00155         << "The second argument passed to this function was "
00156         << "not a preshower cluster!" << std::endl;
00157     } 
00158     */
00159     const reco::PFCluster::REPPoint& pspos = psclus->positionREP();
00160     const reco::PFCluster::REPPoint& eepos = eeclus->positionREP();
00161     // lazy continue based on geometry
00162     if( eeclus->z()*psclus->z() < 0 ) return -1.0;
00163     const double dphi= std::abs(TVector2::Phi_mpi_pi(eepos.phi() - 
00164                                                      pspos.phi()));
00165     if( dphi > 0.6 ) return -1.0;    
00166     const double deta= std::abs(eepos.eta() - pspos.eta());    
00167     if( deta > 0.3 ) return -1.0; 
00168     return LinkByRecHit::testECALAndPSByRecHit(*eeclus,*psclus,false);
00169   }
00170 }
00171 
00172 PFECALSuperClusterAlgo::PFECALSuperClusterAlgo() { }
00173 
00174 void PFECALSuperClusterAlgo::
00175 setPFClusterCalibration(const std::shared_ptr<PFEnergyCalibration>& calib) {
00176   _pfEnergyCalibration = calib;
00177 }
00178 
00179 void PFECALSuperClusterAlgo::
00180 loadAndSortPFClusters(const PFClusterView& clusters,
00181                       const PFClusterView& psclusters) { 
00182   // reset the system for running
00183   superClustersEB_.reset(new reco::SuperClusterCollection);
00184   _clustersEB.clear();
00185   superClustersEE_.reset(new reco::SuperClusterCollection);  
00186   _clustersEE.clear();
00187   _psclustersforee.clear();
00188   
00189   auto clusterPtrs = clusters.ptrVector(); 
00190   //Select PF clusters available for the clustering
00191   for ( auto& cluster : clusterPtrs ){
00192     LogDebug("PFClustering") 
00193       << "Loading PFCluster i="<<cluster.key()
00194       <<" energy="<<cluster->energy()<<std::endl;
00195     
00196     double Ecorr = _pfEnergyCalibration->energyEm(*cluster,
00197                                                   0.0,0.0,
00198                                                   applyCrackCorrections_);
00199     CalibratedClusterPtr calib_cluster(new CalibratedPFCluster(cluster,Ecorr));
00200     switch( cluster->layer() ) {
00201     case PFLayer::ECAL_BARREL:
00202       if( calib_cluster->energy() > threshPFClusterBarrel_ ) {
00203         _clustersEB.push_back(calib_cluster);   
00204       }
00205       break;
00206     case PFLayer::ECAL_ENDCAP:
00207       if( calib_cluster->energy() > threshPFClusterEndcap_ ) {
00208         _clustersEE.push_back(calib_cluster);
00209         _psclustersforee.emplace(calib_cluster->the_ptr(),
00210                                  edm::PtrVector<reco::PFCluster>());
00211       }
00212       break;
00213     default:
00214       break;
00215     }
00216   }
00217   // make the association map of ECAL clusters to preshower clusters  
00218   edm::PtrVector<reco::PFCluster> clusterPtrsPS = psclusters.ptrVector();
00219   double dist = -1.0, min_dist = -1.0;
00220   // match PS clusters to EE clusters, minimum distance to EE is ensured
00221   // since the inner loop is over the EE clusters
00222   for( const auto& psclus : clusterPtrsPS ) {   
00223     if( psclus->energy() < threshPFClusterES_ ) continue;        
00224     switch( psclus->layer() ) { // just in case this isn't the ES...
00225     case PFLayer::PS1:
00226     case PFLayer::PS2:
00227       break;
00228     default:
00229       continue;
00230     }    
00231     edm::Ptr<reco::PFCluster> eematch;
00232     dist = min_dist = -1.0; // reset
00233     for( const auto& eeclus : _clustersEE ) {
00234       dist = testPreshowerDistance(eeclus->the_ptr(),psclus);      
00235       if( dist == -1.0 || (min_dist != -1.0 && dist > min_dist) ) continue;
00236       if( dist < min_dist || min_dist == -1.0 ) {
00237         eematch = eeclus->the_ptr();
00238         min_dist = dist;
00239       }
00240     } // loop on EE clusters      
00241     if( eematch.isNonnull() ) _psclustersforee[eematch].push_back(psclus);
00242   } // loop on PS clusters
00243 
00244   // sort full cluster collections by their calibrated energy
00245   // this will put all the seeds first by construction
00246   GreaterByEt greater;
00247   std::sort(_clustersEB.begin(), _clustersEB.end(), greater);
00248   std::sort(_clustersEE.begin(), _clustersEE.end(), greater);  
00249 }
00250 
00251 void PFECALSuperClusterAlgo::run() {  
00252   // clusterize the EB
00253   buildAllSuperClusters(_clustersEB, threshPFClusterSeedBarrel_);
00254   // clusterize the EE
00255   buildAllSuperClusters(_clustersEE, threshPFClusterSeedEndcap_);
00256 }
00257 
00258 void PFECALSuperClusterAlgo::
00259 buildAllSuperClusters(CalibClusterPtrVector& clusters,
00260                       double seedthresh) {
00261   IsASeed seedable(seedthresh);
00262   // make sure only seeds appear at the front of the list of clusters
00263   std::stable_partition(clusters.begin(),clusters.end(),seedable);
00264   // in each iteration we are working on a list that is already sorted
00265   // in the cluster energy and remains so through each iteration
00266   // NB: since clusters is sorted in loadClusters any_of has O(1)
00267   //     timing until you run out of seeds!  
00268   while( std::any_of(clusters.cbegin(), clusters.cend(), seedable) ) {    
00269     buildSuperCluster(clusters.front(),clusters);
00270   }
00271 }
00272 
00273 void PFECALSuperClusterAlgo::
00274 buildSuperCluster(CalibClusterPtr& seed,
00275                   CalibClusterPtrVector& clusters) {
00276   IsClustered IsClusteredWithSeed(seed,_clustype,_useDynamicDPhi);
00277   IsLinkedByRecHit MatchesSeedByRecHit(seed,satelliteThreshold_,
00278                                        fractionForMajority_,0.1,0.2);
00279   bool isEE = false;
00280   SumPSEnergy sumps1(PFLayer::PS1), sumps2(PFLayer::PS2);  
00281   switch( seed->the_ptr()->layer() ) {
00282   case PFLayer::ECAL_BARREL:
00283     IsClusteredWithSeed.phiwidthSuperCluster_ = phiwidthSuperClusterBarrel_;
00284     IsClusteredWithSeed.etawidthSuperCluster_ = etawidthSuperClusterBarrel_;
00285     edm::LogInfo("PFClustering") << "Building SC number "  
00286                                  << superClustersEB_->size() + 1
00287                                  << " in the ECAL barrel!";
00288     break;
00289   case PFLayer::ECAL_ENDCAP:    
00290     IsClusteredWithSeed.phiwidthSuperCluster_ = phiwidthSuperClusterEndcap_; 
00291     IsClusteredWithSeed.etawidthSuperCluster_ = etawidthSuperClusterEndcap_;
00292     edm::LogInfo("PFClustering") << "Building SC number "  
00293                                  << superClustersEE_->size() + 1
00294                                  << " in the ECAL endcap!" << std::endl;
00295     isEE = true;
00296     break;
00297   default:
00298     break;
00299   }
00300   
00301   // this function shuffles the list of clusters into a list
00302   // where all clustered sub-clusters are at the front 
00303   // and returns a pointer to the first unclustered cluster.
00304   // The relative ordering of clusters is preserved 
00305   // (i.e. both resulting sub-lists are sorted by energy).
00306   auto not_clustered = std::stable_partition(clusters.begin(),clusters.end(),
00307                                              IsClusteredWithSeed);
00308   // satellite cluster merging
00309   // it was found that large clusters can split!
00310   if( doSatelliteClusterMerge_ ) {    
00311     not_clustered = std::stable_partition(not_clustered,clusters.end(),
00312                                           MatchesSeedByRecHit);
00313   }
00314 
00315   if(verbose_) {
00316     edm::LogInfo("PFClustering") << "Dumping cluster detail";
00317     edm::LogVerbatim("PFClustering")
00318       << "\tPassed seed: e = " << seed->energy_nocalib() 
00319       << " eta = " << seed->eta() << " phi = " << seed->phi() 
00320       << std::endl;  
00321     for( auto clus = clusters.cbegin(); clus != not_clustered; ++clus ) {
00322       edm::LogVerbatim("PFClustering") 
00323         << "\t\tClustered cluster: e = " << (*clus)->energy_nocalib() 
00324         << " eta = " << (*clus)->eta() << " phi = " << (*clus)->phi() 
00325         << std::endl;
00326     }
00327     for( auto clus = not_clustered; clus != clusters.end(); ++clus ) {
00328       edm::LogVerbatim("PFClustering") 
00329         << "\tNon-Clustered cluster: e = " << (*clus)->energy_nocalib() 
00330         << " eta = " << (*clus)->eta() << " phi = " << (*clus)->phi() 
00331         << std::endl;
00332     }    
00333   }
00334   // move the clustered clusters out of available cluster list
00335   // and into a temporary vector for building the SC  
00336   CalibratedClusterPtrVector clustered(clusters.begin(),not_clustered);
00337   clusters.erase(clusters.begin(),not_clustered);    
00338   // need the vector of raw pointers for a PF width class
00339   std::vector<const reco::PFCluster*> bare_ptrs;
00340   // calculate necessary parameters and build the SC
00341   double posX(0), posY(0), posZ(0),
00342     rawSCEnergy(0), corrSCEnergy(0), clusterCorrEE(0), 
00343     PS1_clus_sum(0), PS2_clus_sum(0);  
00344   for( auto& clus : clustered ) {
00345     bare_ptrs.push_back(clus->the_ptr().get());
00346       
00347     const double cluseraw = clus->energy_nocalib();
00348     const math::XYZPoint& cluspos = clus->the_ptr()->position();
00349     posX += cluseraw * cluspos.X();
00350     posY += cluseraw * cluspos.Y();
00351     posZ += cluseraw * cluspos.Z();
00352     // update EE calibrated super cluster energies
00353     if( isEE ) {
00354       const auto& psclusters = _psclustersforee[clus->the_ptr()];
00355       PS1_clus_sum = std::accumulate(psclusters.begin(),psclusters.end(),
00356                                      0.0,sumps1);
00357       PS2_clus_sum = std::accumulate(psclusters.begin(),psclusters.end(),
00358                                      0.0,sumps2);
00359       clusterCorrEE = 
00360         _pfEnergyCalibration->energyEm(*(clus->the_ptr()),
00361                                        PS1_clus_sum,PS2_clus_sum,
00362                                        applyCrackCorrections_);
00363       clus->resetCalibratedEnergy(clusterCorrEE);
00364     }
00365 
00366     rawSCEnergy  += cluseraw;
00367     corrSCEnergy += clus->energy();    
00368   }
00369   posX /= rawSCEnergy;
00370   posY /= rawSCEnergy;
00371   posZ /= rawSCEnergy;    
00372   
00373   // now build the supercluster
00374   reco::SuperCluster new_sc(corrSCEnergy,math::XYZPoint(posX,posY,posZ)); 
00375   double ps1_energy(0.0), ps2_energy(0.0), ps_energy(0.0);
00376   new_sc.setSeed(clustered.front()->the_ptr());
00377   for( const auto& clus : clustered ) {
00378     new_sc.addCluster(clus->the_ptr());
00379     auto& hits_and_fractions = clus->the_ptr()->hitsAndFractions();
00380     for( auto& hit_and_fraction : hits_and_fractions ) {
00381       new_sc.addHitAndFraction(hit_and_fraction.first,hit_and_fraction.second);
00382     }
00383     const auto& cluspsassociation = _psclustersforee[clus->the_ptr()];     
00384     // EE rechits should be uniquely matched to sets of pre-shower
00385     // clusters at this point, so we throw an exception if otherwise
00386     for( const auto& psclus : cluspsassociation ) {
00387       auto found_pscluster = std::find(new_sc.preshowerClustersBegin(),
00388                                        new_sc.preshowerClustersEnd(),
00389                                        reco::CaloClusterPtr(psclus));
00390       if( found_pscluster == new_sc.preshowerClustersEnd() ) {
00391         const double psenergy = psclus->energy();
00392         new_sc.addPreshowerCluster(psclus);
00393         ps1_energy += (PFLayer::PS1 == psclus->layer())*psenergy;
00394         ps2_energy += (PFLayer::PS2 == psclus->layer())*psenergy;
00395         ps_energy  += psenergy;
00396       } else {
00397         throw cms::Exception("PFECALSuperClusterAlgo::buildSuperCluster")
00398           << "Found a PS cluster matched to more than one EE cluster!" 
00399           << std::endl << std::hex << psclus.get() << " == " 
00400           << found_pscluster->get() << std::dec << std::endl;
00401       }
00402     }
00403   }
00404   new_sc.setPreshowerEnergy(ps_energy); 
00405   new_sc.setPreshowerEnergyPlane1(ps1_energy);
00406   new_sc.setPreshowerEnergyPlane2(ps2_energy);
00407   
00408   // calculate linearly weighted cluster widths
00409   PFClusterWidthAlgo pfwidth(bare_ptrs);
00410   new_sc.setEtaWidth(pfwidth.pflowEtaWidth());
00411   new_sc.setPhiWidth(pfwidth.pflowPhiWidth());
00412   
00413   // cache the value of the raw energy  
00414   new_sc.rawEnergy();
00415 
00416   // save the super cluster to the appropriate list
00417   switch( seed->the_ptr()->layer() ) {
00418   case PFLayer::ECAL_BARREL:
00419     superClustersEB_->push_back(new_sc);
00420     break;
00421   case PFLayer::ECAL_ENDCAP:    
00422     superClustersEE_->push_back(new_sc);
00423     break;
00424   default:
00425     break;
00426   }
00427 }