CMS 3D CMS Logo

Public Member Functions | Protected Member Functions

PFAlgoTestBenchElectrons Class Reference

Particle Flow Algorithm test bench for the electron team. More...

#include <PFAlgoTestBenchElectrons.h>

Inheritance diagram for PFAlgoTestBenchElectrons:
PFAlgo

List of all members.

Public Member Functions

 PFAlgoTestBenchElectrons ()
 constructor
virtual ~PFAlgoTestBenchElectrons ()
 destructor

Protected Member Functions

virtual void processBlock (const reco::PFBlockRef &blockref, std::list< reco::PFBlockRef > &hcalBlockRefs, std::list< reco::PFBlockRef > &ecalBlockRefs)

Detailed Description

Particle Flow Algorithm test bench for the electron team.

Author:
Florian Beaudette, Daniele Benedetti, Michele Pioppi
Date:
January 2006

Definition at line 18 of file PFAlgoTestBenchElectrons.h.


Constructor & Destructor Documentation

PFAlgoTestBenchElectrons::PFAlgoTestBenchElectrons ( ) [inline]

constructor

Definition at line 23 of file PFAlgoTestBenchElectrons.h.

{}
virtual PFAlgoTestBenchElectrons::~PFAlgoTestBenchElectrons ( ) [inline, virtual]

destructor

Definition at line 26 of file PFAlgoTestBenchElectrons.h.

{}

Member Function Documentation

void PFAlgoTestBenchElectrons::processBlock ( const reco::PFBlockRef blockref,
std::list< reco::PFBlockRef > &  hcalBlockRefs,
std::list< reco::PFBlockRef > &  ecalBlockRefs 
) [protected, virtual]

process one block. can be reimplemented in more sophisticated algorithms

Reimplemented from PFAlgo.

Definition at line 466 of file PFAlgo.cc.

References a, abs, PFAlgo::associatePSClusters(), b, edm::OwnVector< T, P >::begin(), Association::block, alignmentValidation::c1, PFAlgo::calibration_, dtNoiseDBValidation_cfg::cerr, CastorDataFrameFilter_impl::check(), gather_cfg::cout, PFAlgo::debug_, PFAlgo::dptRel_DispVtx_, ECAL, reco::PFBlockElement::ECAL, asciidump::elements, reco::PFCandidate::elementsInBlocks(), reco::LeafCandidate::energy(), eta(), PFAlgo::factors45_, first, PFElectronAlgo::getAllElectronCandidates(), PFElectronAlgo::getElectronCandidates(), PFElectronAlgo::getElectronExtra(), reco::PFBlockElement::GSF, HCAL, reco::PFBlockElement::HCAL, PFLayer::HF_EM, PFLayer::HF_HAD, reco::TrackBase::highPurity, reco::PFBlockElement::HO, i, cuy::ii, getHLTprescales::index, PFElectronAlgo::isElectronValidCandidate(), PFAlgo::isFromSecInt(), PFMuonAlgo::isIsolatedMuon(), muon::isLooseMuon(), reco::isMuon(), edm::Ref< C, T, F >::isNull(), PFPhotonAlgo::isPhotonValidCandidate(), j, reco::PFBlock::LINKTEST_ALL, max(), min, RPCpg::mu, PFAlgo::muonECAL_, PFAlgo::muonHCAL_, PFAlgo::muonHO_, reco::btau::neutralEnergy, PFAlgo::neutralHadronEnergyResolution(), PFAlgo::nSigmaECAL_, PFAlgo::nSigmaHCAL(), PFAlgo::nSigmaTRACK_, convertSQLiteXML::ok, AlCaHLTBitMon_ParallelJobs::p, PFAlgo::pfCandidates_, PFAlgo::pfele_, PFAlgo::pfElectronCandidates_, PFAlgo::pfElectronExtra_, PFAlgo::pfpho_, PFAlgo::pfPhotonCandidates_, PFAlgo::pfPhotonExtra_, PFAlgo::primaryVertex_, reco::PFBlockElement::PS1, reco::PFBlockElement::PS2, PFAlgo::ptError_, PFAlgo::reconstructCluster(), PFAlgo::reconstructTrack(), PFAlgo::rejectTracks_Bad_, PFAlgo::rejectTracks_Step45_, edm::second(), reco::PFCandidate::setEcalEnergy(), reco::PFCandidate::setHcalEnergy(), reco::PFCandidate::setHoEnergy(), reco::PFBlockElementTrack::setPositionAtECALEntrance(), edm::OwnVector< T, P >::size(), createPayload::skip, mathSSE::sqrt(), reco::PFBlockElement::T_FROM_GAMMACONV, PFAlgo::thepfEnergyCalibrationHF_, reco::PFBlockElement::TRACK, reco::btau::trackMomentum, reco::PFBlockElementTrack::trackType(), funct::true, PFAlgo::useHO_, PFAlgo::usePFConversions_, PFAlgo::usePFElectrons_, PFAlgo::usePFPhotons_, X, x, and Gflash::Z.

                                                                    { 
  
  // debug_ = false;
  assert(!blockref.isNull() );
  const reco::PFBlock& block = *blockref;

  typedef std::multimap<double, unsigned>::iterator IE;
  typedef std::multimap<double, std::pair<unsigned,math::XYZVector> >::iterator IS;
  typedef std::multimap<double, std::pair<unsigned,bool> >::iterator IT;
  typedef std::multimap< unsigned, std::pair<double, unsigned> >::iterator II;

  if(debug_) {
    cout<<"#########################################################"<<endl;
    cout<<"#####           Process Block:                      #####"<<endl;
    cout<<"#########################################################"<<endl;
    cout<<block<<endl;
  }


  const edm::OwnVector< reco::PFBlockElement >& elements = block.elements();
  // make a copy of the link data, which will be edited.
  PFBlock::LinkData linkData =  block.linkData();
  
  // keep track of the elements which are still active.
  vector<bool>   active( elements.size(), true );


  // //PFElectrons:
  // usePFElectrons_ external configurable parameter to set the usage of pf electron
  std::vector<reco::PFCandidate> tempElectronCandidates;
  tempElectronCandidates.clear();
  if (usePFElectrons_) {
    if (pfele_->isElectronValidCandidate(blockref,active, primaryVertex_ )){
      // if there is at least a valid candidate is get the vector of pfcandidates
      const std::vector<reco::PFCandidate> PFElectCandidates_(pfele_->getElectronCandidates());
      for ( std::vector<reco::PFCandidate>::const_iterator ec=PFElectCandidates_.begin();   ec != PFElectCandidates_.end(); ++ec )tempElectronCandidates.push_back(*ec);
      
      // (***) We're filling the ElectronCandidates into the PFCandiate collection
      // ..... Once we let PFPhotonAlgo over-write electron-decision, we need to move this to
      // ..... after the PhotonAlgo has run (Fabian)
    }    
    // The vector active is automatically changed (it is passed by ref) in PFElectronAlgo
    // for all the electron candidate      
    pfElectronCandidates_->insert(pfElectronCandidates_->end(), 
                                  pfele_->getAllElectronCandidates().begin(), 
                                  pfele_->getAllElectronCandidates().end()); 

    pfElectronExtra_.insert(pfElectronExtra_.end(), 
                            pfele_->getElectronExtra().begin(),
                            pfele_->getElectronExtra().end());
    
  }
  if( /* --- */ usePFPhotons_ /* --- */ ) {    
    
    if(debug_)
      cout<<endl<<"--------------- entering PFPhotonAlgo ----------------"<<endl;
    vector<PFCandidatePhotonExtra> pfPhotonExtraCand;
    if ( pfpho_->isPhotonValidCandidate(blockref,               // passing the reference to the PFBlock
                                        active,                 // std::vector<bool> containing information about acitivity
                                        pfPhotonCandidates_,    // pointer to candidate vector, to be filled by the routine
                                        pfPhotonExtraCand,      // candidate extra vector, to be filled by the routine   
                                        tempElectronCandidates
                                        //pfElectronCandidates_   // pointer to some auziliary UNTOUCHED FOR NOW
                                        ) ) {
      if(debug_)
        std::cout<< " In this PFBlock we found "<<pfPhotonCandidates_->size()<<" Photon Candidates."<<std::endl;
      
      // CAUTION: In case we want to allow the PhotonAlgo to 'over-write' what the ElectronAlgo did above
      // ........ we should NOT fill the PFCandidate-vector with the electrons above (***)
      
      // Here we need to add all the photon cands to the pfCandidate list
      unsigned int extracand =0;
      PFCandidateCollection::const_iterator cand = pfPhotonCandidates_->begin();      
      for( ; cand != pfPhotonCandidates_->end(); ++cand, ++extracand) {
        pfCandidates_->push_back(*cand);
        pfPhotonExtra_.push_back(pfPhotonExtraCand[extracand]);
      }
      
    } // end of 'if' in case photons are found    
    pfPhotonExtraCand.clear();
    pfPhotonCandidates_->clear();
  } // end of Photon algo
  
  //Lock extra conversion tracks not used by Photon Algo
  if (usePFConversions_  )
    {
      for(unsigned iEle=0; iEle<elements.size(); iEle++) {
        PFBlockElement::Type type = elements[iEle].type();
        if(type==PFBlockElement::TRACK)
          {
            if(elements[iEle].trackRef()->algo() == 12)
              active[iEle]=false;       
            if(elements[iEle].trackRef()->quality(reco::TrackBase::highPurity))continue;
            const reco::PFBlockElementTrack * trackRef = dynamic_cast<const reco::PFBlockElementTrack*>((&elements[iEle]));
            if(!(trackRef->trackType(reco::PFBlockElement::T_FROM_GAMMACONV)))continue;
            if(elements[iEle].convRef().isNonnull())active[iEle]=false;
          }
      }
    }
  for ( std::vector<reco::PFCandidate>::const_iterator ec=tempElectronCandidates.begin();   ec != tempElectronCandidates.end(); ++ec ){
    pfCandidates_->push_back(*ec);  
  } 
  tempElectronCandidates.clear();
  
  
  if(debug_) 
    cout<<endl<<"--------------- loop 1 ------------------"<<endl;

  //COLINFEB16
  // In loop 1, we loop on all elements. 

  // The primary goal is to deal with tracks that are:
  // - not associated to an HCAL cluster
  // - not identified as an electron. 
  // Those tracks should be predominantly relatively low energy charged 
  // hadrons which are not detected in the ECAL. 

  // The secondary goal is to prepare for the next loops
  // - The ecal and hcal elements are sorted in separate vectors
  // which will be used as a base for the corresponding loops.
  // - For tracks which are connected to more than one HCAL cluster, 
  // the links between the track and the cluster are cut for all clusters 
  // but the closest one. 
  // - HF only blocks ( HFEM, HFHAD, HFEM+HFAD) are identified 

  // obsolete comments? 
  // loop1: 
  // - sort ecal and hcal elements in separate vectors
  // - for tracks:
  //       - lock closest ecal cluster 
  //       - cut link to farthest hcal cluster, if more than 1.

  // vectors to store indices to ho, hcal and ecal elements 
  vector<unsigned> hcalIs;
  vector<unsigned> hoIs;
  vector<unsigned> ecalIs;
  vector<unsigned> trackIs;
  vector<unsigned> ps1Is;
  vector<unsigned> ps2Is;

  vector<unsigned> hfEmIs;
  vector<unsigned> hfHadIs;
  

  for(unsigned iEle=0; iEle<elements.size(); iEle++) {
    PFBlockElement::Type type = elements[iEle].type();

    if(debug_ && type != PFBlockElement::BREM ) cout<<endl<<elements[iEle];   

    switch( type ) {
    case PFBlockElement::TRACK:
      if ( active[iEle] ) { 
        trackIs.push_back( iEle );
        if(debug_) cout<<"TRACK, stored index, continue"<<endl;
      }
      break;
    case PFBlockElement::ECAL: 
      if ( active[iEle]  ) { 
        ecalIs.push_back( iEle );
        if(debug_) cout<<"ECAL, stored index, continue"<<endl;
      }
      continue;
    case PFBlockElement::HCAL:
      if ( active[iEle] ) { 
        hcalIs.push_back( iEle );
        if(debug_) cout<<"HCAL, stored index, continue"<<endl;
      }
      continue;
    case PFBlockElement::HO:
      if (useHO_) {
        if ( active[iEle] ) { 
          hoIs.push_back( iEle );
          if(debug_) cout<<"HO, stored index, continue"<<endl;
        }
      }
      continue;
    case PFBlockElement::HFEM:
      if ( active[iEle] ) { 
        hfEmIs.push_back( iEle );
        if(debug_) cout<<"HFEM, stored index, continue"<<endl;
      }
      continue;
    case PFBlockElement::HFHAD:
      if ( active[iEle] ) { 
        hfHadIs.push_back( iEle );
        if(debug_) cout<<"HFHAD, stored index, continue"<<endl;
      }
      continue;
    default:
      continue;
    }

    // we're now dealing with a track
    unsigned iTrack = iEle;
    reco::MuonRef muonRef = elements[iTrack].muonRef();    

    //Check if the track is a primary track of a secondary interaction
    //If that is the case reconstruct a charged hadron noly using that
    //track
    if (active[iTrack] && isFromSecInt(elements[iEle], "primary")){
      bool isPrimaryTrack = elements[iEle].displacedVertexRef(PFBlockElement::T_TO_DISP)->displacedVertexRef()->isTherePrimaryTracks();
      if (isPrimaryTrack) {
        if (debug_) cout << "Primary Track reconstructed alone" << endl;

        unsigned tmpi = reconstructTrack(elements[iEle]);
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEle );
        active[iTrack] = false;
      }
    }


    if(debug_) { 
      if ( !active[iTrack] ) 
        cout << "Already used by electrons, muons, conversions" << endl;
    }
    
    // Track already used as electron, muon, conversion?
    // Added a check on the activated element
    if ( ! active[iTrack] ) continue;
  
    reco::TrackRef trackRef = elements[iTrack].trackRef();
    assert( !trackRef.isNull() ); 

    if (debug_ ) {
      cout <<"PFAlgo:processBlock "<<" "<<trackIs.size()<<" "<<ecalIs.size()<<" "<<hcalIs.size()<<" "<<hoIs.size()<<endl;
    }

    // look for associated elements of all types
    //COLINFEB16 
    // all types of links are considered. 
    // the elements are sorted by increasing distance
    std::multimap<double, unsigned> ecalElems;
    block.associatedElements( iTrack,  linkData,
                              ecalElems ,
                              reco::PFBlockElement::ECAL,
                              reco::PFBlock::LINKTEST_ALL );
    
    std::multimap<double, unsigned> hcalElems;
    block.associatedElements( iTrack,  linkData,
                              hcalElems,
                              reco::PFBlockElement::HCAL,
                              reco::PFBlock::LINKTEST_ALL );

    // When a track has no HCAL cluster linked, but another track is linked to the same
    // ECAL cluster and an HCAL cluster, link the track to the HCAL cluster for 
    // later analysis
    if ( hcalElems.empty() && !ecalElems.empty() ) {
      // debug_ = true;
      unsigned ntt = 1;
      unsigned index = ecalElems.begin()->second;
      std::multimap<double, unsigned> sortedTracks;
      block.associatedElements( index,  linkData,
                                sortedTracks,
                                reco::PFBlockElement::TRACK,
                                reco::PFBlock::LINKTEST_ALL );
      // std::cout << "The ECAL is linked to " << sortedTracks.size() << std::endl;

      // Loop over all tracks
      for(IE ie = sortedTracks.begin(); ie != sortedTracks.end(); ++ie ) {
        unsigned jTrack = ie->second;
        // std::cout << "Track " << jTrack << std::endl;
        // Track must be active
        if ( !active[jTrack] ) continue;
        //std::cout << "Active " << std::endl;
        
        // The loop is on the other tracks !
        if ( jTrack == iTrack ) continue;
        //std::cout << "A different track ! " << std::endl;
        
        // Check if the ECAL closest to this track is the current ECAL
        // Otherwise ignore this track in the neutral energy determination
        std::multimap<double, unsigned> sortedECAL;
        block.associatedElements( jTrack,  linkData,
                                  sortedECAL,
                                  reco::PFBlockElement::ECAL,
                                  reco::PFBlock::LINKTEST_ALL );
        if ( sortedECAL.begin()->second != index ) continue;
        //std::cout << "With closest ECAL identical " << std::endl;

        
        // Check if this track is also linked to an HCAL 
        std::multimap<double, unsigned> sortedHCAL;
        block.associatedElements( jTrack,  linkData,
                                  sortedHCAL,
                                  reco::PFBlockElement::HCAL,
                                  reco::PFBlock::LINKTEST_ALL );
        if ( !sortedHCAL.size() ) continue;
        //std::cout << "With an HCAL cluster " << sortedHCAL.begin()->first << std::endl;
        ntt++;
        
        // In that case establish a link with the first track 
        block.setLink( iTrack, 
                       sortedHCAL.begin()->second, 
                       sortedECAL.begin()->first, 
                       linkData,
                       PFBlock::LINKTEST_RECHIT );
        
      } // End other tracks
      
      // Redefine HCAL elements 
      block.associatedElements( iTrack,  linkData,
                                hcalElems,
                                reco::PFBlockElement::HCAL,
                                reco::PFBlock::LINKTEST_ALL );

      if ( debug_ && hcalElems.size() ) 
        std::cout << "Track linked back to HCAL due to ECAL sharing with other tracks" << std::endl;
    }
    
    //MICHELE 
    //TEMPORARY SOLUTION FOR ELECTRON REJECTION IN PFTAU
    //COLINFEB16 
    // in case particle flow electrons are not reconstructed, 
    // the mva_e_pi of the charged hadron will be set to 1 
    // if a GSF element is associated to the current TRACK element
    // This information will be used in the electron rejection for tau ID.
    std::multimap<double,unsigned> gsfElems;
    if (usePFElectrons_ == false) {
      block.associatedElements( iTrack,  linkData,
                                gsfElems ,
                                reco::PFBlockElement::GSF );
    }
    //

    if(hcalElems.empty() && debug_) {
      cout<<"no hcal element connected to track "<<iTrack<<endl;
    }

    // will now loop on associated elements ... 
    // typedef std::multimap<double, unsigned>::iterator IE;
   
    bool hcalFound = false;

    if(debug_) 
      cout<<"now looping on elements associated to the track"<<endl;
    
    // ... first on associated ECAL elements
    // Check if there is still a free ECAL for this track
    for(IE ie = ecalElems.begin(); ie != ecalElems.end(); ++ie ) {
      
      unsigned index = ie->second;
      // Sanity checks and optional printout
      PFBlockElement::Type type = elements[index].type();
      if(debug_) {
        double  dist  = ie->first;
        cout<<"\telement "<<elements[index]<<" linked with distance = "<< dist <<endl;
        if ( ! active[index] ) cout << "This ECAL is already used - skip it" << endl;      
      }
      assert( type == PFBlockElement::ECAL );

      // This ECAL is not free (taken by an electron?) - just skip it
      if ( ! active[index] ) continue;      

      // Flag ECAL clusters for which the corresponding track is not linked to an HCAL cluster

      //reco::PFClusterRef clusterRef = elements[index].clusterRef();
      //assert( !clusterRef.isNull() );
      if( !hcalElems.empty() && debug_)
        cout<<"\t\tat least one hcal element connected to the track."
            <<" Sparing Ecal cluster for the hcal loop"<<endl; 

    } //loop print ecal elements

  
    // tracks which are not linked to an HCAL 
    // are reconstructed now. 

    if( hcalElems.empty() ) {
      
      if ( debug_ ) 
        std::cout << "Now deals with tracks linked to no HCAL clusters" << std::endl; 
      // vector<unsigned> elementIndices;
      // elementIndices.push_back(iTrack); 

      // The track momentum
      double trackMomentum = elements[iTrack].trackRef()->p();      
      if ( debug_ ) 
        std::cout << elements[iTrack] << std::endl; 
      
      // Is it a "tight" muon ? In that case assume no 
      //Track momentum corresponds to the calorimeter
      //energy for now
      bool thisIsAMuon = PFMuonAlgo::isMuon(elements[iTrack]);
      if ( thisIsAMuon ) trackMomentum = 0.;

      // If it is not a muon check if Is it a fake track ?
      //Michalis: I wonder if we should convert this to dpt/pt?
      bool rejectFake = false;
      if ( !thisIsAMuon  && elements[iTrack].trackRef()->ptError() > ptError_ ) { 

        double deficit = trackMomentum; 
        double resol = neutralHadronEnergyResolution(trackMomentum,
                                                     elements[iTrack].trackRef()->eta());
        resol *= trackMomentum;

        if ( !ecalElems.empty() ) { 
          unsigned thisEcal = ecalElems.begin()->second;
          reco::PFClusterRef clusterRef = elements[thisEcal].clusterRef();
          deficit -= clusterRef->energy();
          resol = neutralHadronEnergyResolution(trackMomentum,
                                                clusterRef->positionREP().Eta());
          resol *= trackMomentum;
        }

        bool isPrimary = isFromSecInt(elements[iTrack], "primary");

        if ( deficit > nSigmaTRACK_*resol && !isPrimary) { 
          rejectFake = true;
          active[iTrack] = false;
          if ( debug_ )  
            std::cout << elements[iTrack] << std::endl
                      << "is probably a fake (1) --> lock the track" 
                      << std::endl;
        }
      }

      if ( rejectFake ) continue;

      // Create a track candidate       
      // unsigned tmpi = reconstructTrack( elements[iTrack] );
      //active[iTrack] = false;
      std::vector<unsigned> tmpi;
      std::vector<unsigned> kTrack;
      
      // Some cleaning : secondary tracks without calo's and large momentum must be fake
      double Dpt = trackRef->ptError();

      if ( rejectTracks_Step45_ && ecalElems.empty() && 
           trackMomentum > 30. && Dpt > 0.5 && 
           ( trackRef->algo() == TrackBase::iter4 || 
             trackRef->algo() == TrackBase::iter5 || 
             trackRef->algo() == TrackBase::iter6 ) ) {

        //
        double dptRel = Dpt/trackRef->pt()*100;
        bool isPrimaryOrSecondary = isFromSecInt(elements[iTrack], "all");

        if ( isPrimaryOrSecondary && dptRel < dptRel_DispVtx_) continue;
        unsigned nHits =  elements[iTrack].trackRef()->hitPattern().trackerLayersWithMeasurement();
        unsigned int NLostHit = trackRef->hitPattern().trackerLayersWithoutMeasurement();

        if ( debug_ ) 
          std::cout << "A track (algo = " << trackRef->algo() << ") with momentum " << trackMomentum 
                    << " / " << elements[iTrack].trackRef()->pt() << " +/- " << Dpt 
                    << " / " << elements[iTrack].trackRef()->eta() 
                    << " without any link to ECAL/HCAL and with " << nHits << " (" << NLostHit 
                    << ") hits (lost hits) has been cleaned" << std::endl;
        active[iTrack] = false;
        continue;
      }


      tmpi.push_back(reconstructTrack( elements[iTrack]));

      kTrack.push_back(iTrack);
      active[iTrack] = false;

      // No ECAL cluster either ... continue...
      if ( ecalElems.empty() ) { 
        (*pfCandidates_)[tmpi[0]].setEcalEnergy( 0., 0. );
        (*pfCandidates_)[tmpi[0]].setHcalEnergy( 0., 0. );
        (*pfCandidates_)[tmpi[0]].setHoEnergy( 0., 0. );
        (*pfCandidates_)[tmpi[0]].setPs1Energy( 0 );
        (*pfCandidates_)[tmpi[0]].setPs2Energy( 0 );
        (*pfCandidates_)[tmpi[0]].addElementInBlock( blockref, kTrack[0] );
        continue;
      }
          
      // Look for closest ECAL cluster
      unsigned thisEcal = ecalElems.begin()->second;
      reco::PFClusterRef clusterRef = elements[thisEcal].clusterRef();
      if ( debug_ ) std::cout << " is associated to " << elements[thisEcal] << std::endl;


      // Set ECAL energy for muons
      if ( thisIsAMuon ) { 
        (*pfCandidates_)[tmpi[0]].setEcalEnergy( clusterRef->energy(),
                                                 std::min(clusterRef->energy(), muonECAL_[0]) );
        (*pfCandidates_)[tmpi[0]].setHcalEnergy( 0., 0. );
        (*pfCandidates_)[tmpi[0]].setHoEnergy( 0., 0. );
        (*pfCandidates_)[tmpi[0]].setPs1Energy( 0 );
        (*pfCandidates_)[tmpi[0]].setPs2Energy( 0 );
        (*pfCandidates_)[tmpi[0]].addElementInBlock( blockref, kTrack[0] );
      }
      
      double slopeEcal = 1.;
      bool connectedToEcal = false;
      unsigned iEcal = 99999;
      double calibEcal = 0.;
      double calibHcal = 0.;
      double totalEcal = thisIsAMuon ? -muonECAL_[0] : 0.;

      // Consider charged particles closest to the same ECAL cluster
      std::multimap<double, unsigned> sortedTracks;
      block.associatedElements( thisEcal,  linkData,
                                sortedTracks,
                                reco::PFBlockElement::TRACK,
                                reco::PFBlock::LINKTEST_ALL );
    
      for(IE ie = sortedTracks.begin(); ie != sortedTracks.end(); ++ie ) {
        unsigned jTrack = ie->second;

        // Don't consider already used tracks
        if ( !active[jTrack] ) continue;

        // The loop is on the other tracks !
        if ( jTrack == iTrack ) continue;
        
        // Check if the ECAL cluster closest to this track is 
        // indeed the current ECAL cluster. Only tracks not 
        // linked to an HCAL cluster are considered here
        std::multimap<double, unsigned> sortedECAL;
        block.associatedElements( jTrack,  linkData,
                                  sortedECAL,
                                  reco::PFBlockElement::ECAL,
                                  reco::PFBlock::LINKTEST_ALL );
        if ( sortedECAL.begin()->second != thisEcal ) continue;

        // Check if this track is a muon
        bool thatIsAMuon = PFMuonAlgo::isMuon(elements[jTrack]);


        // Check if this track is not a fake
        bool rejectFake = false;
        reco::TrackRef trackRef = elements[jTrack].trackRef();
        if ( !thatIsAMuon && trackRef->ptError() > ptError_) { 
          double deficit = trackMomentum + trackRef->p() - clusterRef->energy();
          double resol = nSigmaTRACK_*neutralHadronEnergyResolution(trackMomentum+trackRef->p(),
                                                                    clusterRef->positionREP().Eta());
          resol *= (trackMomentum+trackRef->p());
          if ( deficit > nSigmaTRACK_*resol ) { 
            rejectFake = true;
            kTrack.push_back(jTrack);
            active[jTrack] = false;
            if ( debug_ )  
              std::cout << elements[jTrack] << std::endl
                        << "is probably a fake (2) --> lock the track" 
                        << std::endl; 
          }
        }
        if ( rejectFake ) continue;
        
        
        // Otherwise, add this track momentum to the total track momentum
        /* */
        // reco::TrackRef trackRef = elements[jTrack].trackRef();
        if ( !thatIsAMuon ) {   
          if ( debug_ ) 
            std::cout << "Track momentum increased from " << trackMomentum << " GeV "; 
          trackMomentum += trackRef->p();
          if ( debug_ ) {
            std::cout << "to " << trackMomentum << " GeV." << std::endl; 
            std::cout << "with " << elements[jTrack] << std::endl;
          }
        } else { 
          totalEcal -= muonECAL_[0];
          totalEcal = std::max(totalEcal, 0.);
        }

        // And create a charged particle candidate !

        tmpi.push_back(reconstructTrack( elements[jTrack] ));


        kTrack.push_back(jTrack);
        active[jTrack] = false;

        if ( thatIsAMuon ) { 
          (*pfCandidates_)[tmpi.back()].setEcalEnergy(clusterRef->energy(),
                                                      std::min(clusterRef->energy(),muonECAL_[0]));
          (*pfCandidates_)[tmpi.back()].setHcalEnergy( 0., 0. );
          (*pfCandidates_)[tmpi.back()].setHoEnergy( 0., 0. );
          (*pfCandidates_)[tmpi.back()].setPs1Energy( 0 );
          (*pfCandidates_)[tmpi.back()].setPs2Energy( 0 );
          (*pfCandidates_)[tmpi.back()].addElementInBlock( blockref, kTrack.back() );
        }
      }


      if ( debug_ ) std::cout << "Loop over all associated ECAL clusters" << std::endl; 
      // Loop over all ECAL linked clusters ordered by increasing distance.
      for(IE ie = ecalElems.begin(); ie != ecalElems.end(); ++ie ) {
        
        unsigned index = ie->second;
        PFBlockElement::Type type = elements[index].type();
        assert( type == PFBlockElement::ECAL );
        if ( debug_ ) std::cout << elements[index] << std::endl;
        
        // Just skip clusters already taken
        if ( debug_ && ! active[index] ) std::cout << "is not active  - ignore " << std::endl;
        if ( ! active[index] ) continue;

        // Just skip this cluster if it's closer to another track
        /* */
        block.associatedElements( index,  linkData,
                                  sortedTracks,
                                  reco::PFBlockElement::TRACK,
                                  reco::PFBlock::LINKTEST_ALL );
        bool skip = true;
        for (unsigned ic=0; ic<kTrack.size();++ic) {
          if ( sortedTracks.begin()->second == kTrack[ic] ) { 
            skip = false;
            break;
          }
        }
        if ( debug_ && skip ) std::cout << "is closer to another track - ignore " << std::endl;
        if ( skip ) continue;
        /* */
                
        // The corresponding PFCluster and energy
        reco::PFClusterRef clusterRef = elements[index].clusterRef();
        assert( !clusterRef.isNull() );

        if ( debug_ ) {
          double dist = ie->first;
          std::cout <<"Ecal cluster with raw energy = " << clusterRef->energy() 
                    <<" linked with distance = " << dist << std::endl;
        }
        /*
        double dist = ie->first;
        if ( !connectedToEcal && dist > 0.1 ) {
          std::cout << "Warning - first ECAL cluster at a distance of " << dist << "from the closest track!" << std::endl;
          cout<<"\telement "<<elements[index]<<" linked with distance = "<< dist <<endl;
          cout<<"\telement "<<elements[iTrack]<<" linked with distance = "<< dist <<endl;
        }
        */ 

        // Check the presence of preshower clusters in the vicinity
        // Preshower cluster closer to another ECAL cluster are ignored.
        vector<double> ps1Ene(1,static_cast<double>(0.));
        associatePSClusters(index, reco::PFBlockElement::PS1, block, elements, linkData, active, ps1Ene);
        vector<double> ps2Ene(1,static_cast<double>(0.));
        associatePSClusters(index, reco::PFBlockElement::PS2, block, elements, linkData, active, ps2Ene);
        
        // Get the energy calibrated (for photons)
        bool crackCorrection = false;
        double ecalEnergy = calibration_->energyEm(*clusterRef,ps1Ene,ps2Ene,crackCorrection);
        if ( debug_ )
          std::cout << "Corrected ECAL(+PS) energy = " << ecalEnergy << std::endl;

        // Since the electrons were found beforehand, this track must be a hadron. Calibrate 
        // the energy under the hadron hypothesis.
        totalEcal += ecalEnergy;
        double previousCalibEcal = calibEcal;
        double previousSlopeEcal = slopeEcal;
        calibEcal = std::max(totalEcal,0.);
        calibHcal = 0.;
        calibration_->energyEmHad(trackMomentum,calibEcal,calibHcal,
                                  clusterRef->positionREP().Eta(),
                                  clusterRef->positionREP().Phi());
        if ( totalEcal > 0.) slopeEcal = calibEcal/totalEcal;

        if ( debug_ )
          std::cout << "The total calibrated energy so far amounts to = " << calibEcal << std::endl;
        

        // Stop the loop when adding more ECAL clusters ruins the compatibility
        if ( connectedToEcal && calibEcal - trackMomentum >= 0. ) {
        // if ( connectedToEcal && calibEcal - trackMomentum >=
        //     nSigmaECAL_*neutralHadronEnergyResolution(trackMomentum,clusterRef->positionREP().Eta())  ) { 
          calibEcal = previousCalibEcal;
          slopeEcal = previousSlopeEcal;
          totalEcal = calibEcal/slopeEcal;

          // Turn this last cluster in a photon 
          // (The PS clusters are already locked in "associatePSClusters")
          active[index] = false;

          // Find the associated tracks
          std::multimap<double, unsigned> assTracks;
          block.associatedElements( index,  linkData,
                                    assTracks,
                                    reco::PFBlockElement::TRACK,
                                    reco::PFBlock::LINKTEST_ALL );


          unsigned tmpe = reconstructCluster( *clusterRef, ecalEnergy ); 
          (*pfCandidates_)[tmpe].setEcalEnergy( clusterRef->energy(), ecalEnergy );
          (*pfCandidates_)[tmpe].setHcalEnergy( 0., 0. );
          (*pfCandidates_)[tmpe].setHoEnergy( 0., 0. );
          (*pfCandidates_)[tmpe].setPs1Energy( ps1Ene[0] );
          (*pfCandidates_)[tmpe].setPs2Energy( ps2Ene[0] );
          (*pfCandidates_)[tmpe].addElementInBlock( blockref, index );
          // Check that there is at least one track
          if(assTracks.size()) {
            (*pfCandidates_)[tmpe].addElementInBlock( blockref, assTracks.begin()->second );
            
            // Assign the position of the track at the ECAL entrance
            const math::XYZPointF& chargedPosition = 
              dynamic_cast<const reco::PFBlockElementTrack*>(&elements[assTracks.begin()->second])->positionAtECALEntrance();
            (*pfCandidates_)[tmpe].setPositionAtECALEntrance(chargedPosition);
          }
          break;
        }

        // Lock used clusters.
        connectedToEcal = true;
        iEcal = index;
        active[index] = false;
        for (unsigned ic=0; ic<tmpi.size();++ic)  
          (*pfCandidates_)[tmpi[ic]].addElementInBlock( blockref, iEcal ); 


      } // Loop ecal elements
    
      bool bNeutralProduced = false;
  
      // Create a photon if the ecal energy is too large
      if( connectedToEcal ) {
        reco::PFClusterRef pivotalRef = elements[iEcal].clusterRef();

        double neutralEnergy = calibEcal - trackMomentum;

        /*
        // Check if there are other tracks linked to that ECAL
        for(IE ie = sortedTracks.begin(); ie != sortedTracks.end() && neutralEnergy > 0; ++ie ) {
          unsigned jTrack = ie->second;
          
          // The loop is on the other tracks !
          if ( jTrack == iTrack ) continue;

          // Check if the ECAL closest to this track is the current ECAL
          // Otherwise ignore this track in the neutral energy determination
          std::multimap<double, unsigned> sortedECAL;
          block.associatedElements( jTrack,  linkData,
                                    sortedECAL,
                                    reco::PFBlockElement::ECAL,
                                    reco::PFBlock::LINKTEST_ALL );
          if ( sortedECAL.begin()->second != iEcal ) continue;
          
          // Check if this track is also linked to an HCAL 
          // (in which case it goes to the next loop and is ignored here)
          std::multimap<double, unsigned> sortedHCAL;
          block.associatedElements( jTrack,  linkData,
                                    sortedHCAL,
                                    reco::PFBlockElement::HCAL,
                                    reco::PFBlock::LINKTEST_ALL );
          if ( sortedHCAL.size() ) continue;
          
          // Otherwise, subtract this track momentum from the ECAL energy 
          reco::TrackRef trackRef = elements[jTrack].trackRef();
          neutralEnergy -= trackRef->p();

        } // End other tracks
        */
        
        // Add a photon is the energy excess is large enough
        double resol = neutralHadronEnergyResolution(trackMomentum,pivotalRef->positionREP().Eta());
        resol *= trackMomentum;
        if ( neutralEnergy > std::max(0.5,nSigmaECAL_*resol) ) {
          neutralEnergy /= slopeEcal;
          unsigned tmpj = reconstructCluster( *pivotalRef, neutralEnergy ); 
          (*pfCandidates_)[tmpj].setEcalEnergy( pivotalRef->energy(), neutralEnergy );
          (*pfCandidates_)[tmpj].setHcalEnergy( 0., 0. );
          (*pfCandidates_)[tmpj].setHoEnergy( 0., 0. );
          (*pfCandidates_)[tmpj].setPs1Energy( 0. );
          (*pfCandidates_)[tmpj].setPs2Energy( 0. );
          (*pfCandidates_)[tmpj].addElementInBlock(blockref, iEcal);
          bNeutralProduced = true;
          for (unsigned ic=0; ic<kTrack.size();++ic) 
            (*pfCandidates_)[tmpj].addElementInBlock( blockref, kTrack[ic] ); 
        } // End neutral energy

        // Set elements in blocks and ECAL energies to all tracks
        for (unsigned ic=0; ic<tmpi.size();++ic) { 
          
          // Skip muons
          if ( (*pfCandidates_)[tmpi[ic]].particleId() == reco::PFCandidate::mu ) continue; 

          double fraction = (*pfCandidates_)[tmpi[ic]].trackRef()->p()/trackMomentum;
          double ecalCal = bNeutralProduced ? 
            (calibEcal-neutralEnergy*slopeEcal)*fraction : calibEcal*fraction;
          double ecalRaw = totalEcal*fraction;

          if (debug_) cout << "The fraction after photon supression is " << fraction << " calibrated ecal = " << ecalCal << endl;

          (*pfCandidates_)[tmpi[ic]].setEcalEnergy( ecalRaw, ecalCal );
          (*pfCandidates_)[tmpi[ic]].setHcalEnergy( 0., 0. );
          (*pfCandidates_)[tmpi[ic]].setHoEnergy( 0., 0. );
          (*pfCandidates_)[tmpi[ic]].setPs1Energy( 0 );
          (*pfCandidates_)[tmpi[ic]].setPs2Energy( 0 );
          (*pfCandidates_)[tmpi[ic]].addElementInBlock( blockref, kTrack[ic] );
        }

      } // End connected ECAL

      // Fill the element_in_block for tracks that are eventually linked to no ECAL clusters at all.
      for (unsigned ic=0; ic<tmpi.size();++ic) { 
        const PFCandidate& pfc = (*pfCandidates_)[tmpi[ic]];
        const PFCandidate::ElementsInBlocks& eleInBlocks = pfc.elementsInBlocks();
        if ( eleInBlocks.size() == 0 ) { 
          if ( debug_ )std::cout << "Single track / Fill element in block! " << std::endl;
          (*pfCandidates_)[tmpi[ic]].addElementInBlock( blockref, kTrack[ic] );
        }
      }

    }


    // In case several HCAL elements are linked to this track, 
    // unlinking all of them except the closest. 
    for(IE ie = hcalElems.begin(); ie != hcalElems.end(); ++ie ) {
      
      unsigned index = ie->second;
      
      PFBlockElement::Type type = elements[index].type();

      if(debug_) {
        double dist = block.dist(iTrack,index,linkData,reco::PFBlock::LINKTEST_ALL);
        cout<<"\telement "<<elements[index]<<" linked with distance "<< dist <<endl;
      }

      assert( type == PFBlockElement::HCAL );
      
      // all hcal clusters except the closest 
      // will be unlinked from the track
      if( !hcalFound ) { // closest hcal
        if(debug_) 
          cout<<"\t\tclosest hcal cluster, doing nothing"<<endl;
        
        hcalFound = true;
        
        // active[index] = false;
        // hcalUsed.push_back( index );
      }
      else { // other associated hcal
        // unlink from the track
        if(debug_) 
          cout<<"\t\tsecondary hcal cluster. unlinking"<<endl;
        block.setLink( iTrack, index, -1., linkData,
                       PFBlock::LINKTEST_RECHIT );
      }
    } //loop hcal elements   
  } // end of loop 1 on elements iEle of any type



  // deal with HF. 
  if( !(hfEmIs.empty() &&  hfHadIs.empty() ) ) {
    // there is at least one HF element in this block. 
    // so all elements must be HF.
    assert( hfEmIs.size() + hfHadIs.size() == elements.size() );

    if( elements.size() == 1 ) {
      //Auguste: HAD-only calibration here
      reco::PFClusterRef clusterRef = elements[0].clusterRef();
      double energyHF = 0.;
      double uncalibratedenergyHF = 0.;
      unsigned tmpi = 0;
      switch( clusterRef->layer() ) {
      case PFLayer::HF_EM:
        // do EM-only calibration here
        energyHF = clusterRef->energy();
        uncalibratedenergyHF = energyHF;
        if(thepfEnergyCalibrationHF_->getcalibHF_use()==true){
          energyHF = thepfEnergyCalibrationHF_->energyEm(uncalibratedenergyHF,
                                                         clusterRef->positionREP().Eta(),
                                                         clusterRef->positionREP().Phi()); 
        }
        tmpi = reconstructCluster( *clusterRef, energyHF );     
        (*pfCandidates_)[tmpi].setEcalEnergy( uncalibratedenergyHF, energyHF );
        (*pfCandidates_)[tmpi].setHcalEnergy( 0., 0.);
        (*pfCandidates_)[tmpi].setHoEnergy( 0., 0.);
        (*pfCandidates_)[tmpi].setPs1Energy( 0. );
        (*pfCandidates_)[tmpi].setPs2Energy( 0. );
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, hfEmIs[0] );
        //std::cout << "HF EM alone ! " << energyHF << std::endl;
        break;
      case PFLayer::HF_HAD:
        // do HAD-only calibration here
        energyHF = clusterRef->energy();
        uncalibratedenergyHF = energyHF;
        if(thepfEnergyCalibrationHF_->getcalibHF_use()==true){
          energyHF = thepfEnergyCalibrationHF_->energyHad(uncalibratedenergyHF,
                                                         clusterRef->positionREP().Eta(),
                                                         clusterRef->positionREP().Phi()); 
        }
        tmpi = reconstructCluster( *clusterRef, energyHF );     
        (*pfCandidates_)[tmpi].setHcalEnergy( uncalibratedenergyHF, energyHF );
        (*pfCandidates_)[tmpi].setEcalEnergy( 0., 0.);
        (*pfCandidates_)[tmpi].setHoEnergy( 0., 0.);
        (*pfCandidates_)[tmpi].setPs1Energy( 0. );
        (*pfCandidates_)[tmpi].setPs2Energy( 0. );
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, hfHadIs[0] );
        //std::cout << "HF Had alone ! " << energyHF << std::endl;
        break;
      default:
        assert(0);
      }
    }
    else if(  elements.size() == 2 ) {
      //Auguste: EM + HAD calibration here
      reco::PFClusterRef c0 = elements[0].clusterRef();
      reco::PFClusterRef c1 = elements[1].clusterRef();
      // 2 HF elements. Must be in each layer.
      reco::PFClusterRef cem = c1;
      reco::PFClusterRef chad = c0;
      
      if( c0->layer()== PFLayer::HF_EM ) {
        if ( c1->layer()== PFLayer::HF_HAD ) {
          cem = c0; chad = c1;
        } else { 
          assert(0);
        }
        // do EM+HAD calibration here
        double energyHfEm = cem->energy();
        double energyHfHad = chad->energy();
        double uncalibratedenergyHFEm = energyHfEm;
        double uncalibratedenergyHFHad = energyHfHad;
        if(thepfEnergyCalibrationHF_->getcalibHF_use()==true){

          energyHfEm = thepfEnergyCalibrationHF_->energyEmHad(uncalibratedenergyHFEm,
                                                             0.0,
                                                             c0->positionREP().Eta(),
                                                             c0->positionREP().Phi()); 
          energyHfHad = thepfEnergyCalibrationHF_->energyEmHad(0.0,
                                                              uncalibratedenergyHFHad,
                                                              c1->positionREP().Eta(),
                                                              c1->positionREP().Phi()); 
        }
        unsigned tmpi = reconstructCluster( *chad, energyHfEm+energyHfHad );     
        (*pfCandidates_)[tmpi].setEcalEnergy( uncalibratedenergyHFEm, energyHfEm );
        (*pfCandidates_)[tmpi].setHcalEnergy( uncalibratedenergyHFHad, energyHfHad);
        (*pfCandidates_)[tmpi].setHoEnergy( 0., 0.);
        (*pfCandidates_)[tmpi].setPs1Energy( 0. );
        (*pfCandidates_)[tmpi].setPs2Energy( 0. );
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, hfEmIs[0] );
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, hfHadIs[0] );
        //std::cout << "HF EM+HAD found ! " << energyHfEm << " " << energyHfHad << std::endl;

      }
      else {
        cerr<<"Warning: 2 elements, but not 1 HFEM and 1 HFHAD"<<endl;
        cerr<<block<<endl;
//      assert( c1->layer()== PFLayer::HF_EM &&
//              c0->layer()== PFLayer::HF_HAD );
      }      
    }
    else {
      // 1 HF element in the block, 
      // but number of elements not equal to 1 or 2 
      cerr<<"Warning: HF, but n elem different from 1 or 2"<<endl;
      cerr<<block<<endl;
//       assert(0);
//       cerr<<"not ready for navigation in the HF!"<<endl;
    }
  }



  if(debug_) { 
    cout<<endl;
    cout<<endl<<"--------------- loop hcal ---------------------"<<endl;
  }
  
  // track information:
  // trackRef
  // ecal energy
  // hcal energy
  // rescale 

  for(unsigned i=0; i<hcalIs.size(); i++) {
    
    unsigned iHcal= hcalIs[i];    
    PFBlockElement::Type type = elements[iHcal].type();

    assert( type == PFBlockElement::HCAL );

    if(debug_) cout<<endl<<elements[iHcal]<<endl;

    // vector<unsigned> elementIndices;
    // elementIndices.push_back(iHcal);

    // associated tracks
    std::multimap<double, unsigned> sortedTracks;
    block.associatedElements( iHcal,  linkData,
                              sortedTracks,
                              reco::PFBlockElement::TRACK,
                              reco::PFBlock::LINKTEST_ALL );

    std::multimap< unsigned, std::pair<double, unsigned> > associatedEcals;

    std::map< unsigned, std::pair<double, double> > associatedPSs;
 
    std::multimap<double, std::pair<unsigned,bool> > associatedTracks;
    
    // A temporary maps for ECAL satellite clusters
    std::multimap<double,std::pair<unsigned,math::XYZVector> > ecalSatellites;
    std::pair<unsigned,math::XYZVector> fakeSatellite = make_pair(iHcal,math::XYZVector(0.,0.,0.));
    ecalSatellites.insert( make_pair(-1., fakeSatellite) );

    std::multimap< unsigned, std::pair<double, unsigned> > associatedHOs;
    
    PFClusterRef hclusterref = elements[iHcal].clusterRef();
    assert(!hclusterref.isNull() );


    //if there is no track attached to that HCAL, then do not
    //reconstruct an HCAL alone, check if it can be recovered 
    //first
    
    // if no associated tracks, create a neutral hadron
    //if(sortedTracks.empty() ) {

    if( sortedTracks.empty() ) {
      if(debug_) 
        cout<<"\tno associated tracks, keep for later"<<endl;
      continue;
    }

    // Lock the HCAL cluster
    active[iHcal] = false;


    // in the following, tracks are associated to this hcal cluster.
    // will look for an excess of energy in the calorimeters w/r to 
    // the charged energy, and turn this excess into a neutral hadron or 
    // a photon.

    if(debug_) cout<<"\t"<<sortedTracks.size()<<" associated tracks:"<<endl;

    double totalChargedMomentum = 0;
    double sumpError2 = 0.;
    double totalHO = 0.;
    double totalEcal = 0.;
    double totalHcal = hclusterref->energy();
    vector<double> hcalP;
    vector<double> hcalDP;
    vector<unsigned> tkIs;
    double maxDPovP = -9999.;
    
    //Keep track of how much energy is assigned to calorimeter-vs-track energy/momentum excess
    vector< unsigned > chargedHadronsIndices;
    vector< unsigned > chargedHadronsInBlock;
    double mergedNeutralHadronEnergy = 0;
    double mergedPhotonEnergy = 0;
    double muonHCALEnergy = 0.;
    double muonECALEnergy = 0.;
    double muonHCALError = 0.;
    double muonECALError = 0.;
    unsigned nMuons = 0; 


    math::XYZVector photonAtECAL(0.,0.,0.);
    math::XYZVector hadronDirection(hclusterref->position().X(),
                                    hclusterref->position().Y(),
                                    hclusterref->position().Z());
    hadronDirection = hadronDirection.Unit();
    math::XYZVector hadronAtECAL = totalHcal * hadronDirection;

    // Loop over all tracks associated to this HCAL cluster
    for(IE ie = sortedTracks.begin(); ie != sortedTracks.end(); ++ie ) {

      unsigned iTrack = ie->second;

      // Check the track has not already been used (e.g., in electrons, conversions...)
      if ( !active[iTrack] ) continue;
      // Sanity check 1
      PFBlockElement::Type type = elements[iTrack].type();
      assert( type == reco::PFBlockElement::TRACK );
      // Sanity check 2
      reco::TrackRef trackRef = elements[iTrack].trackRef();
      assert( !trackRef.isNull() ); 

      // The direction at ECAL entrance
      const math::XYZPointF& chargedPosition = 
        dynamic_cast<const reco::PFBlockElementTrack*>(&elements[iTrack])->positionAtECALEntrance();
      math::XYZVector chargedDirection(chargedPosition.X(),chargedPosition.Y(),chargedPosition.Z());
      chargedDirection = chargedDirection.Unit();

      // look for ECAL elements associated to iTrack (associated to iHcal)
      std::multimap<double, unsigned> sortedEcals;
      block.associatedElements( iTrack,  linkData,
                                sortedEcals,
                                reco::PFBlockElement::ECAL,
                                reco::PFBlock::LINKTEST_ALL );

      if(debug_) cout<<"\t\t\tnumber of Ecal elements linked to this track: "
                     <<sortedEcals.size()<<endl;     

      // look for HO elements associated to iTrack (associated to iHcal)
      std::multimap<double, unsigned> sortedHOs;
      if (useHO_) {
        block.associatedElements( iTrack,  linkData,
                                  sortedHOs,
                                  reco::PFBlockElement::HO,
                                  reco::PFBlock::LINKTEST_ALL );

      }
      if(debug_) 
        cout<<"PFAlgo : number of HO elements linked to this track: "
            <<sortedHOs.size()<<endl;
      
      // Create a PF Candidate right away if the track is a tight muon
      reco::MuonRef muonRef = elements[iTrack].muonRef();


      bool thisIsAMuon = PFMuonAlgo::isMuon(elements[iTrack]);
      bool thisIsAnIsolatedMuon = PFMuonAlgo::isIsolatedMuon(elements[iTrack]);
      bool thisIsALooseMuon = false;


      if(!thisIsAMuon ) {
        thisIsALooseMuon = PFMuonAlgo::isLooseMuon(elements[iTrack]);
      }


      if ( thisIsAMuon ) {
        if ( debug_ ) { 
          std::cout << "\t\tThis track is identified as a muon - remove it from the stack" << std::endl;
          std::cout << "\t\t" << elements[iTrack] << std::endl;
        }

        // Create a muon.

        unsigned tmpi = reconstructTrack( elements[iTrack] );


        (*pfCandidates_)[tmpi].addElementInBlock( blockref, iTrack );
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHcal );
        double muonHcal = std::min(muonHCAL_[0]+muonHCAL_[1],totalHcal);

        // if muon is isolated and muon momentum exceeds the calo energy, absorb the calo energy        
        // rationale : there has been a EM showering by the muon in the calorimeter - or the coil -
        // and we don't want to double count the energy 
        bool letMuonEatCaloEnergy = false;

        if(thisIsAnIsolatedMuon){
          // The factor 1.3 is the e/pi factor in HCAL...
          double totalCaloEnergy = totalHcal / 1.30;
          unsigned iEcal = 0;
          if( !sortedEcals.empty() ) { 
            iEcal = sortedEcals.begin()->second; 
            PFClusterRef eclusterref = elements[iEcal].clusterRef();
            totalCaloEnergy += eclusterref->energy();
          }

          if (useHO_) {
            // The factor 1.3 is assumed to be the e/pi factor in HO, too.
            unsigned iHO = 0;
            if( !sortedHOs.empty() ) { 
              iHO = sortedHOs.begin()->second; 
              PFClusterRef eclusterref = elements[iHO].clusterRef();
              totalCaloEnergy += eclusterref->energy() / 1.30;
            }
          }

          // std::cout << "muon p / total calo = " << muonRef->p() << " "  << (pfCandidates_->back()).p() << " " << totalCaloEnergy << std::endl;
          //if(muonRef->p() > totalCaloEnergy ) letMuonEatCaloEnergy = true;
          if( (pfCandidates_->back()).p() > totalCaloEnergy ) letMuonEatCaloEnergy = true;
        }

        if(letMuonEatCaloEnergy) muonHcal = totalHcal;
        double muonEcal =0.;
        unsigned iEcal = 0;
        if( !sortedEcals.empty() ) { 
          iEcal = sortedEcals.begin()->second; 
          PFClusterRef eclusterref = elements[iEcal].clusterRef();
          (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEcal);
          muonEcal = std::min(muonECAL_[0]+muonECAL_[1],eclusterref->energy());
          if(letMuonEatCaloEnergy) muonEcal = eclusterref->energy();
          // If the muon expected energy accounts for the whole ecal cluster energy, lock the ecal cluster
          if ( eclusterref->energy() - muonEcal  < 0.2 ) active[iEcal] = false;
          (*pfCandidates_)[tmpi].setEcalEnergy(eclusterref->energy(), muonEcal);
        }
        unsigned iHO = 0;
        double muonHO =0.;
        if (useHO_) {
          if( !sortedHOs.empty() ) { 
            iHO = sortedHOs.begin()->second; 
            PFClusterRef hoclusterref = elements[iHO].clusterRef();
            (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHO);
            muonHO = std::min(muonHO_[0]+muonHO_[1],hoclusterref->energy());
            if(letMuonEatCaloEnergy) muonHO = hoclusterref->energy();
            // If the muon expected energy accounts for the whole HO cluster energy, lock the HO cluster
            if ( hoclusterref->energy() - muonHO  < 0.2 ) active[iHO] = false;      
            (*pfCandidates_)[tmpi].setHcalEnergy(totalHcal, muonHcal);
            (*pfCandidates_)[tmpi].setHoEnergy(hoclusterref->energy(), muonHO);
          }
        } else {
          (*pfCandidates_)[tmpi].setHcalEnergy(totalHcal, muonHcal);
        }

        if(letMuonEatCaloEnergy){
          muonHCALEnergy += totalHcal;
          if (useHO_) muonHCALEnergy +=muonHO;
          muonHCALError += 0.;
          muonECALEnergy += muonEcal;
          muonECALError += 0.;
          photonAtECAL -= muonEcal*chargedDirection;
          hadronAtECAL -= totalHcal*chargedDirection;
          if ( !sortedEcals.empty() ) active[iEcal] = false;
          active[iHcal] = false;
          if (useHO_ && !sortedHOs.empty() ) active[iHO] = false;
        }
        else{
        // Estimate of the energy deposit & resolution in the calorimeters
          muonHCALEnergy += muonHCAL_[0];
          muonHCALError += muonHCAL_[1]*muonHCAL_[1];
          if ( muonHO > 0. ) { 
            muonHCALEnergy += muonHO_[0];
            muonHCALError += muonHO_[1]*muonHO_[1];
          }
          muonECALEnergy += muonECAL_[0];
          muonECALError += muonECAL_[1]*muonECAL_[1];
          // ... as well as the equivalent "momentum" at ECAL entrance
          photonAtECAL -= muonECAL_[0]*chargedDirection;
          hadronAtECAL -= muonHCAL_[0]*chargedDirection;
        }

        // Remove it from the stack
        active[iTrack] = false;
        // Go to next track
        continue;
      }
 
      //

      if(debug_) cout<<"\t\t"<<elements[iTrack]<<endl;

      // introduce  tracking errors 
      double trackMomentum = trackRef->p();
      totalChargedMomentum += trackMomentum; 

      // If the track is not a tight muon, but still resembles a muon
      // keep it for later for blocks with too large a charged energy
      if ( thisIsALooseMuon && !thisIsAMuon ) nMuons += 1;      

      // ... and keep anyway the pt error for possible fake rejection
      // ... blow up errors of 5th anf 4th iteration, to reject those
      // ... tracks first (in case it's needed)
      double Dpt = trackRef->ptError();
      double blowError = 1.;
      switch (trackRef->algo()) {
      case TrackBase::ctf:
      case TrackBase::iter0:
      case TrackBase::iter1:
      case TrackBase::iter2:
      case TrackBase::iter3:
      case TrackBase::iter4:
        blowError = 1.;
        break;
      case TrackBase::iter5:
        blowError = factors45_[0];
        break;
      case TrackBase::iter6:
        blowError = factors45_[1];
        break;
      default:
        blowError = 1E9;
        break;
      }
      // except if it is from an interaction
      bool isPrimaryOrSecondary = isFromSecInt(elements[iTrack], "all");

      if ( isPrimaryOrSecondary ) blowError = 1.;

      std::pair<unsigned,bool> tkmuon = make_pair(iTrack,thisIsALooseMuon);
      associatedTracks.insert( make_pair(-Dpt*blowError, tkmuon) );     
 
      // Also keep the total track momentum error for comparison with the calo energy
      double Dp = trackRef->qoverpError()*trackMomentum*trackMomentum;
      sumpError2 += Dp*Dp;

      bool connectedToEcal = false; // Will become true if there is at least one ECAL cluster connected
      if( !sortedEcals.empty() ) 
        { // start case: at least one ecal element associated to iTrack
          
          // Loop over all connected ECAL clusters
          for ( IE iec=sortedEcals.begin(); 
                iec!=sortedEcals.end(); ++iec ) {

            unsigned iEcal = iec->second;
            double dist = iec->first;
            
            // Ignore ECAL cluters already used
            if( !active[iEcal] ) { 
              if(debug_) cout<<"\t\tcluster locked"<<endl;          
              continue;
            }

            // Sanity checks
            PFBlockElement::Type type = elements[ iEcal ].type();
            assert( type == PFBlockElement::ECAL );
            PFClusterRef eclusterref = elements[iEcal].clusterRef();
            assert(!eclusterref.isNull() );
              
            // Check if this ECAL is not closer to another track - ignore it in that case
            std::multimap<double, unsigned> sortedTracksEcal;
            block.associatedElements( iEcal,  linkData,
                                      sortedTracksEcal,
                                      reco::PFBlockElement::TRACK,
                                      reco::PFBlock::LINKTEST_ALL );
            unsigned jTrack = sortedTracksEcal.begin()->second;
            if ( jTrack != iTrack ) continue; 

            // double chi2Ecal = block.chi2(jTrack,iEcal,linkData,
            //                              reco::PFBlock::LINKTEST_ALL);
            double distEcal = block.dist(jTrack,iEcal,linkData,
                                         reco::PFBlock::LINKTEST_ALL);
                                         
            // totalEcal += eclusterref->energy();
            // float ecalEnergyUnCalibrated = eclusterref->energy();
            //std::cout << "Ecal Uncalibrated " << ecalEnergyUnCalibrated << std::endl;
            
            // check the presence of preshower clusters in the vicinity
            vector<double> ps1Ene(1,static_cast<double>(0.));
            associatePSClusters(iEcal, reco::PFBlockElement::PS1, 
                                block, elements, linkData, active, 
                                ps1Ene);
            vector<double> ps2Ene(1,static_cast<double>(0.));
            associatePSClusters(iEcal, reco::PFBlockElement::PS2, 
                                block, elements, linkData, active, 
                                ps2Ene); 
            std::pair<double,double> psEnes = make_pair(ps1Ene[0],
                                                        ps2Ene[0]);
            associatedPSs.insert(make_pair(iEcal,psEnes));
              
            // Calibrate the ECAL energy for photons
            bool crackCorrection = false;
            float ecalEnergyCalibrated = calibration_->energyEm(*eclusterref,ps1Ene,ps2Ene,crackCorrection);
            math::XYZVector photonDirection(eclusterref->position().X(),
                                            eclusterref->position().Y(),
                                            eclusterref->position().Z());
            photonDirection = photonDirection.Unit();

            if ( !connectedToEcal ) { // This is the closest ECAL cluster - will add its energy later
              
              if(debug_) cout<<"\t\t\tclosest: "
                             <<elements[iEcal]<<endl;
              
              connectedToEcal = true;
              // PJ 1st-April-09 : To be done somewhere !!! (Had to comment it, but it is needed)
              // currentChargedHadron.addElementInBlock( blockref, iEcal );
       
              std::pair<unsigned,math::XYZVector> satellite = 
                make_pair(iEcal,ecalEnergyCalibrated*photonDirection);
              ecalSatellites.insert( make_pair(-1., satellite) );

            } else { // Keep satellite clusters for later
              
              std::pair<unsigned,math::XYZVector> satellite = 
                make_pair(iEcal,ecalEnergyCalibrated*photonDirection);
              ecalSatellites.insert( make_pair(dist, satellite) );
              
            }
            
            std::pair<double, unsigned> associatedEcal 
              = make_pair( distEcal, iEcal );
            associatedEcals.insert( make_pair(iTrack, associatedEcal) );

          } // End loop ecal associated to iTrack
        } // end case: at least one ecal element associated to iTrack

      if( useHO_ && !sortedHOs.empty() ) 
        { // start case: at least one ho element associated to iTrack
          
          // Loop over all connected HO clusters
          for ( IE ieho=sortedHOs.begin(); ieho!=sortedHOs.end(); ++ieho ) {

            unsigned iHO = ieho->second;
            double distHO = ieho->first;
            
            // Ignore HO cluters already used
            if( !active[iHO] ) { 
              if(debug_) cout<<"\t\tcluster locked"<<endl;          
              continue;
            }
            
            // Sanity checks
            PFBlockElement::Type type = elements[ iHO ].type();
            assert( type == PFBlockElement::HO );
            PFClusterRef hoclusterref = elements[iHO].clusterRef();
            assert(!hoclusterref.isNull() );
            
            // Check if this HO is not closer to another track - ignore it in that case
            std::multimap<double, unsigned> sortedTracksHO;
            block.associatedElements( iHO,  linkData,
                                      sortedTracksHO,
                                      reco::PFBlockElement::TRACK,
                                      reco::PFBlock::LINKTEST_ALL );
            unsigned jTrack = sortedTracksHO.begin()->second;
            if ( jTrack != iTrack ) continue; 

            // double chi2HO = block.chi2(jTrack,iHO,linkData,
            //                              reco::PFBlock::LINKTEST_ALL);
            //double distHO = block.dist(jTrack,iHO,linkData,
            //                 reco::PFBlock::LINKTEST_ALL);
            
            // Increment the total energy by the energy of this HO cluster
            totalHO += hoclusterref->energy();
            active[iHO] = false;
            // Keep track for later reference in the PFCandidate.
            std::pair<double, unsigned> associatedHO
              = make_pair( distHO, iHO );
            associatedHOs.insert( make_pair(iTrack, associatedHO) );
            
          } // End loop ho associated to iTrack
        } // end case: at least one ho element associated to iTrack
      
      
    } // end loop on tracks associated to hcal element iHcal

    // Include totalHO in totalHCAL for the time being (it will be calibrated as HCAL energy)
    totalHcal += totalHO;
    
    // test compatibility between calo and tracker. //////////////

    double caloEnergy = 0.;
    double slopeEcal = 1.0;
    double calibEcal = 0.;
    double calibHcal = 0.;
    hadronDirection = hadronAtECAL.Unit();

    // Determine the expected calo resolution from the total charged momentum
    double Caloresolution = neutralHadronEnergyResolution( totalChargedMomentum, hclusterref->positionREP().Eta());    
    Caloresolution *= totalChargedMomentum;
    // Account for muons
    Caloresolution = std::sqrt(Caloresolution*Caloresolution + muonHCALError + muonECALError);
    totalEcal -= std::min(totalEcal,muonECALEnergy);
    totalHcal -= std::min(totalHcal,muonHCALEnergy);
    if ( totalEcal < 1E-9 ) photonAtECAL = math::XYZVector(0.,0.,0.);
    if ( totalHcal < 1E-9 ) hadronAtECAL = math::XYZVector(0.,0.,0.);

    // Loop over all ECAL satellites, starting for the closest to the various tracks
    // and adding other satellites until saturation of the total track momentum
    // Note : for code simplicity, the first element of the loop is the HCAL cluster
    // with 0 energy in the ECAL
    for ( IS is = ecalSatellites.begin(); is != ecalSatellites.end(); ++is ) { 

      // Add the energy of this ECAL cluster
      double previousCalibEcal = calibEcal;
      double previousCalibHcal = calibHcal;
      double previousCaloEnergy = caloEnergy;
      double previousSlopeEcal = slopeEcal;
      math::XYZVector previousHadronAtECAL = hadronAtECAL;
      //
      totalEcal += sqrt(is->second.second.Mag2());
      photonAtECAL += is->second.second;
      calibEcal = std::max(0.,totalEcal);
      calibHcal = std::max(0.,totalHcal);
      hadronAtECAL = calibHcal * hadronDirection;
      // Calibrate ECAL and HCAL energy under the hadron hypothesis.
      calibration_->energyEmHad(totalChargedMomentum,calibEcal,calibHcal,
                                hclusterref->positionREP().Eta(),
                                hclusterref->positionREP().Phi());
      caloEnergy = calibEcal+calibHcal;
      if ( totalEcal > 0.) slopeEcal = calibEcal/totalEcal;

      hadronAtECAL = calibHcal * hadronDirection; 
      
      // Continue looping until all closest clusters are exhausted and as long as
      // the calorimetric energy does not saturate the total momentum.
      if ( is->first < 0. || caloEnergy - totalChargedMomentum <= 0. ) { 
        if(debug_) cout<<"\t\t\tactive, adding "<<is->second.second
                       <<" to ECAL energy, and locking"<<endl;
        active[is->second.first] = false;
        continue;
      }

      // Otherwise, do not consider the last cluster examined and exit.
      // active[is->second.first] = true;
      totalEcal -= sqrt(is->second.second.Mag2());
      photonAtECAL -= is->second.second;
      calibEcal = previousCalibEcal;
      calibHcal = previousCalibHcal;
      hadronAtECAL = previousHadronAtECAL;
      slopeEcal = previousSlopeEcal;
      caloEnergy = previousCaloEnergy;

      break;

    }

    // Sanity check !
    assert(caloEnergy>=0);


    // And now check for hadronic energy excess...

    //colin: resolution should be measured on the ecal+hcal case. 
    // however, the result will be close. 
    // double Caloresolution = neutralHadronEnergyResolution( caloEnergy );
    // Caloresolution *= caloEnergy;
    // PJ The resolution is on the expected charged calo energy !
    //double Caloresolution = neutralHadronEnergyResolution( totalChargedMomentum, hclusterref->positionREP().Eta());
    //Caloresolution *= totalChargedMomentum;
    // that of the charged particles linked to the cluster!
    double TotalError = sqrt(sumpError2 + Caloresolution*Caloresolution);

    /* */
    if ( totalChargedMomentum - caloEnergy > nSigmaTRACK_*Caloresolution ) {    

      /* 
      cout<<"\tCompare Calo Energy to total charged momentum "<<endl;
      cout<<"\t\tsum p    = "<<totalChargedMomentum<<" +- "<<sqrt(sumpError2)<<endl;
      cout<<"\t\tsum ecal = "<<totalEcal<<endl;
      cout<<"\t\tsum hcal = "<<totalHcal<<endl;
      cout<<"\t\t => Calo Energy = "<<caloEnergy<<" +- "<<Caloresolution<<endl;
      cout<<"\t\t => Calo Energy- total charged momentum = "
         <<caloEnergy-totalChargedMomentum<<" +- "<<TotalError<<endl;
      cout<<endl;
      cout << "\t\tNumber/momentum of muons found in the block : " << nMuons << std::endl;
      */
      // First consider loose muons
      if ( nMuons > 0 ) { 
        for ( IT it = associatedTracks.begin(); it != associatedTracks.end(); ++it ) { 
          unsigned iTrack = it->second.first;
          // Only active tracks
          if ( !active[iTrack] ) continue;
          // Only muons
          if ( !it->second.second ) continue;

          double trackMomentum = elements[it->second.first].trackRef()->p();

          // look for ECAL elements associated to iTrack (associated to iHcal)
          std::multimap<double, unsigned> sortedEcals;
          block.associatedElements( iTrack,  linkData,
                                    sortedEcals,
                                    reco::PFBlockElement::ECAL,
                                    reco::PFBlock::LINKTEST_ALL );
          std::multimap<double, unsigned> sortedHOs;
          block.associatedElements( iTrack,  linkData,
                                    sortedHOs,
                                    reco::PFBlockElement::HO,
                                    reco::PFBlock::LINKTEST_ALL );

          //Here allow for loose muons! 
          unsigned tmpi = reconstructTrack( elements[iTrack],true);

          (*pfCandidates_)[tmpi].addElementInBlock( blockref, iTrack );
          (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHcal );
          double muonHcal = std::min(muonHCAL_[0]+muonHCAL_[1],totalHcal-totalHO);
          double muonHO = 0.;
          (*pfCandidates_)[tmpi].setHcalEnergy(totalHcal,muonHcal);
          if( !sortedEcals.empty() ) { 
            unsigned iEcal = sortedEcals.begin()->second; 
            PFClusterRef eclusterref = elements[iEcal].clusterRef();
            (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEcal);
            double muonEcal = std::min(muonECAL_[0]+muonECAL_[1],eclusterref->energy());
            (*pfCandidates_)[tmpi].setEcalEnergy(eclusterref->energy(),muonEcal);
          }
          if( useHO_ && !sortedHOs.empty() ) { 
            unsigned iHO = sortedHOs.begin()->second; 
            PFClusterRef hoclusterref = elements[iHO].clusterRef();
            (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHO);
            muonHO = std::min(muonHO_[0]+muonHO_[1],hoclusterref->energy());
            (*pfCandidates_)[tmpi].setHcalEnergy(totalHcal-totalHO,muonHcal);
            (*pfCandidates_)[tmpi].setHoEnergy(hoclusterref->energy(),muonHO);
          }
          // Remove it from the block
          const math::XYZPointF& chargedPosition = 
            dynamic_cast<const reco::PFBlockElementTrack*>(&elements[it->second.first])->positionAtECALEntrance();        
          math::XYZVector chargedDirection(chargedPosition.X(), chargedPosition.Y(), chargedPosition.Z());
          chargedDirection = chargedDirection.Unit();
          totalChargedMomentum -= trackMomentum;
          // Update the calo energies
          if ( totalEcal > 0. ) 
            calibEcal -= std::min(calibEcal,muonECAL_[0]*calibEcal/totalEcal);
          if ( totalHcal > 0. ) 
            calibHcal -= std::min(calibHcal,muonHCAL_[0]*calibHcal/totalHcal);
          totalEcal -= std::min(totalEcal,muonECAL_[0]);
          totalHcal -= std::min(totalHcal,muonHCAL_[0]);
          if ( totalEcal > muonECAL_[0] ) photonAtECAL -= muonECAL_[0] * chargedDirection;
          if ( totalHcal > muonHCAL_[0] ) hadronAtECAL -= muonHCAL_[0]*calibHcal/totalHcal * chargedDirection;
          caloEnergy = calibEcal+calibHcal;
          muonHCALEnergy += muonHCAL_[0];
          muonHCALError += muonHCAL_[1]*muonHCAL_[1];
          if ( muonHO > 0. ) { 
            muonHCALEnergy += muonHO_[0];
            muonHCALError += muonHO_[1]*muonHO_[1];
            if ( totalHcal > 0. ) { 
              calibHcal -= std::min(calibHcal,muonHO_[0]*calibHcal/totalHcal);
              totalHcal -= std::min(totalHcal,muonHO_[0]);
            }
          }
          muonECALEnergy += muonECAL_[0];
          muonECALError += muonECAL_[1]*muonECAL_[1];
          active[iTrack] = false;
          // Stop the loop whenever enough muons are removed
          //Commented out: Keep looking for muons since they often come in pairs -Matt
          //if ( totalChargedMomentum < caloEnergy ) break;     
        }
        // New calo resolution.
        Caloresolution = neutralHadronEnergyResolution( totalChargedMomentum, hclusterref->positionREP().Eta());    
        Caloresolution *= totalChargedMomentum;
        Caloresolution = std::sqrt(Caloresolution*Caloresolution + muonHCALError + muonECALError);
      }
    }
    /*    
    if(debug_){
      cout<<"\tBefore Cleaning "<<endl;
      cout<<"\tCompare Calo Energy to total charged momentum "<<endl;
      cout<<"\t\tsum p    = "<<totalChargedMomentum<<" +- "<<sqrt(sumpError2)<<endl;
      cout<<"\t\tsum ecal = "<<totalEcal<<endl;
      cout<<"\t\tsum hcal = "<<totalHcal<<endl;
      cout<<"\t\t => Calo Energy = "<<caloEnergy<<" +- "<<Caloresolution<<endl;
      cout<<"\t\t => Calo Energy- total charged momentum = "
          <<caloEnergy-totalChargedMomentum<<" +- "<<TotalError<<endl;
      cout<<endl;
    }
    */

    // Second consider bad tracks (if still needed after muon removal)
    unsigned corrTrack = 10000000;
    double corrFact = 1.;

    if (rejectTracks_Bad_ && 
        totalChargedMomentum - caloEnergy > nSigmaTRACK_*Caloresolution) { 
      
      for ( IT it = associatedTracks.begin(); it != associatedTracks.end(); ++it ) { 
        unsigned iTrack = it->second.first;
        // Only active tracks
        if ( !active[iTrack] ) continue;
        const reco::TrackRef& trackref = elements[it->second.first].trackRef();

        double dptRel = fabs(it->first)/trackref->pt()*100;
        bool isSecondary = isFromSecInt(elements[iTrack], "secondary");
        bool isPrimary = isFromSecInt(elements[iTrack], "primary");

        if ( isSecondary && dptRel < dptRel_DispVtx_ ) continue;
        // Consider only bad tracks
        if ( fabs(it->first) < ptError_ ) break;
        // What would become the block charged momentum if this track were removed
        double wouldBeTotalChargedMomentum = 
          totalChargedMomentum - trackref->p();
        // Reject worst tracks, as long as the total charged momentum 
        // is larger than the calo energy

        if ( wouldBeTotalChargedMomentum > caloEnergy ) { 

          if (debug_ && isSecondary) {
            cout << "In bad track rejection step dptRel = " << dptRel << " dptRel_DispVtx_ = " << dptRel_DispVtx_ << endl;
            cout << "The calo energy would be still smaller even without this track but it is attached to a NI"<< endl;
          }


          if(isPrimary || (isSecondary && dptRel < dptRel_DispVtx_)) continue;
          active[iTrack] = false;
          totalChargedMomentum = wouldBeTotalChargedMomentum;
          if ( debug_ )
            std::cout << "\tElement  " << elements[iTrack] 
                      << " rejected (Dpt = " << -it->first 
                      << " GeV/c, algo = " << trackref->algo() << ")" << std::endl;
        // Just rescale the nth worst track momentum to equalize the calo energy
        } else {        
          if(isPrimary) break;
          corrTrack = iTrack;
          corrFact = (caloEnergy - wouldBeTotalChargedMomentum)/elements[it->second.first].trackRef()->p();
          if ( trackref->p()*corrFact < 0.05 ) { 
            corrFact = 0.;
            active[iTrack] = false;
          }
          totalChargedMomentum -= trackref->p()*(1.-corrFact);
          if ( debug_ ) 
            std::cout << "\tElement  " << elements[iTrack] 
                      << " (Dpt = " << -it->first 
                      << " GeV/c, algo = " << trackref->algo() 
                      << ") rescaled by " << corrFact 
                      << " Now the total charged momentum is " << totalChargedMomentum  << endl;
          break;
        }
      }
    }

    // New determination of the calo and track resolution avec track deletion/rescaling.
    Caloresolution = neutralHadronEnergyResolution( totalChargedMomentum, hclusterref->positionREP().Eta());    
    Caloresolution *= totalChargedMomentum;
    Caloresolution = std::sqrt(Caloresolution*Caloresolution + muonHCALError + muonECALError);

    // Check if the charged momentum is still very inconsistent with the calo measurement.
    // In this case, just drop all tracks from 4th and 5th iteration linked to this block
    
    if ( rejectTracks_Step45_ &&
         sortedTracks.size() > 1 && 
         totalChargedMomentum - caloEnergy > nSigmaTRACK_*Caloresolution ) { 
      for ( IT it = associatedTracks.begin(); it != associatedTracks.end(); ++it ) { 
        unsigned iTrack = it->second.first;
        reco::TrackRef trackref = elements[iTrack].trackRef();
        if ( !active[iTrack] ) continue;

        double dptRel = fabs(it->first)/trackref->pt()*100;
        bool isPrimaryOrSecondary = isFromSecInt(elements[iTrack], "all");




        if (isPrimaryOrSecondary && dptRel < dptRel_DispVtx_) continue;
        //
        switch (trackref->algo()) {
        case TrackBase::ctf:
        case TrackBase::iter0:
        case TrackBase::iter1:
        case TrackBase::iter2:
        case TrackBase::iter3:
        case TrackBase::iter4:
          break;
        case TrackBase::iter5:
        case TrackBase::iter6:
          active[iTrack] = false;       
          totalChargedMomentum -= trackref->p();
          
          if ( debug_ ) 
            std::cout << "\tElement  " << elements[iTrack] 
                      << " rejected (Dpt = " << -it->first 
                      << " GeV/c, algo = " << trackref->algo() << ")" << std::endl;
          break;
        default:
          break;
        }
      }
    }

    // New determination of the calo and track resolution avec track deletion/rescaling.
    Caloresolution = neutralHadronEnergyResolution( totalChargedMomentum, hclusterref->positionREP().Eta());    
    Caloresolution *= totalChargedMomentum;
    Caloresolution = std::sqrt(Caloresolution*Caloresolution + muonHCALError + muonECALError);

    // Make PF candidates with the remaining tracks in the block
    sumpError2 = 0.;
    for ( IT it = associatedTracks.begin(); it != associatedTracks.end(); ++it ) { 
      unsigned iTrack = it->second.first;
      if ( !active[iTrack] ) continue;
      reco::TrackRef trackRef = elements[iTrack].trackRef();
      double trackMomentum = trackRef->p();
      double Dp = trackRef->qoverpError()*trackMomentum*trackMomentum;
      unsigned tmpi = reconstructTrack( elements[iTrack] );


      (*pfCandidates_)[tmpi].addElementInBlock( blockref, iTrack );
      (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHcal );
      std::pair<II,II> myEcals = associatedEcals.equal_range(iTrack);
      for (II ii=myEcals.first; ii!=myEcals.second; ++ii ) { 
        unsigned iEcal = ii->second.second;
        if ( active[iEcal] ) continue;
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEcal );
      }
      
      if (useHO_) {
        std::pair<II,II> myHOs = associatedHOs.equal_range(iTrack);
        for (II ii=myHOs.first; ii!=myHOs.second; ++ii ) { 
          unsigned iHO = ii->second.second;
          if ( active[iHO] ) continue;
          (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHO );
        }
      }

      if ( iTrack == corrTrack ) { 
        (*pfCandidates_)[tmpi].rescaleMomentum(corrFact);
        trackMomentum *= corrFact;
      }
      chargedHadronsIndices.push_back( tmpi );
      chargedHadronsInBlock.push_back( iTrack );
      active[iTrack] = false;
      hcalP.push_back(trackMomentum);
      hcalDP.push_back(Dp);
      if (Dp/trackMomentum > maxDPovP) maxDPovP = Dp/trackMomentum;
      sumpError2 += Dp*Dp;
    }

    // The total uncertainty of the difference Calo-Track
    TotalError = sqrt(sumpError2 + Caloresolution*Caloresolution);

    if ( debug_ ) {
      cout<<"\tCompare Calo Energy to total charged momentum "<<endl;
      cout<<"\t\tsum p    = "<<totalChargedMomentum<<" +- "<<sqrt(sumpError2)<<endl;
      cout<<"\t\tsum ecal = "<<totalEcal<<endl;
      cout<<"\t\tsum hcal = "<<totalHcal<<endl;
      cout<<"\t\t => Calo Energy = "<<caloEnergy<<" +- "<<Caloresolution<<endl;
      cout<<"\t\t => Calo Energy- total charged momentum = "
          <<caloEnergy-totalChargedMomentum<<" +- "<<TotalError<<endl;
      cout<<endl;
    }

    /* */

    double nsigma = nSigmaHCAL(totalChargedMomentum,hclusterref->positionREP().Eta());
    //double nsigma = nSigmaHCAL(caloEnergy,hclusterref->positionREP().Eta());
    if ( abs(totalChargedMomentum-caloEnergy)<nsigma*TotalError ) {

      // deposited caloEnergy compatible with total charged momentum
      // if tracking errors are large take weighted average
      
      if(debug_) {
        cout<<"\t\tcase 1: COMPATIBLE "
            <<"|Calo Energy- total charged momentum| = "
            <<abs(caloEnergy-totalChargedMomentum)
            <<" < "<<nsigma<<" x "<<TotalError<<endl;
        if (maxDPovP < 0.1 )
          cout<<"\t\t\tmax DP/P = "<< maxDPovP 
              <<" less than 0.1: do nothing "<<endl;
        else 
          cout<<"\t\t\tmax DP/P = "<< maxDPovP 
              <<" >  0.1: take weighted averages "<<endl;
      }
      
      // if max DP/P < 10%  do nothing
      if (maxDPovP > 0.1) {
      
        // for each track associated to hcal
        //      int nrows = tkIs.size();
        int nrows = chargedHadronsIndices.size();
        TMatrixTSym<double> a (nrows);
        TVectorD b (nrows);
        TVectorD check(nrows);
        double sigma2E = Caloresolution*Caloresolution;
        for(int i=0; i<nrows; i++) {
          double sigma2i = hcalDP[i]*hcalDP[i];
          if (debug_){
            cout<<"\t\t\ttrack associated to hcal "<<i 
                <<" P = "<<hcalP[i]<<" +- "
                <<hcalDP[i]<<endl;
          }
          a(i,i) = 1./sigma2i + 1./sigma2E;
          b(i) = hcalP[i]/sigma2i + caloEnergy/sigma2E;
          for(int j=0; j<nrows; j++) {
            if (i==j) continue;
            a(i,j) = 1./sigma2E;
          } // end loop on j
        } // end loop on i
        
        // solve ax = b
        //if (debug_){// debug1
        //cout<<" matrix a(i,j) "<<endl;
        //a.Print();
        //cout<<" vector b(i) "<<endl;
        //b.Print();
        //} // debug1
        TDecompChol decomp(a);
        bool ok = false;
        TVectorD x = decomp.Solve(b, ok);
        // for each track create a PFCandidate track
        // with a momentum rescaled to weighted average
        if (ok) {
          for (int i=0; i<nrows; i++){
            //      unsigned iTrack = trackInfos[i].index;
            unsigned ich = chargedHadronsIndices[i];
            double rescaleFactor =  x(i)/hcalP[i];
            (*pfCandidates_)[ich].rescaleMomentum( rescaleFactor );

            if(debug_){
              cout<<"\t\t\told p "<<hcalP[i]
                  <<" new p "<<x(i) 
                  <<" rescale "<<rescaleFactor<<endl;
            }
          }
        }
        else {
          cerr<<"not ok!"<<endl;
          assert(0);
        }
      }
    }

    else if( caloEnergy > totalChargedMomentum ) {
      
      //case 2: caloEnergy > totalChargedMomentum + nsigma*TotalError
      //there is an excess of energy in the calos
      //create a neutral hadron or a photon

      /*        
      //If it's isolated don't create neutrals since the energy deposit is always coming from a showering muon
      bool thisIsAnIsolatedMuon = false;
      for(IE ie = sortedTracks.begin(); ie != sortedTracks.end(); ++ie ) {
        unsigned iTrack = ie->second;
        if(PFMuonAlgo::isIsolatedMuon(elements[iTrack].muonRef())) thisIsAnIsolatedMuon = true; 
      }
      
      if(thisIsAnIsolatedMuon){
        if(debug_)cout<<" Not looking for neutral b/c this is an isolated muon "<<endl;
        break;
      }
      */
      double eNeutralHadron = caloEnergy - totalChargedMomentum;
      double ePhoton = (caloEnergy - totalChargedMomentum) / slopeEcal;

      if(debug_) {
        if(!sortedTracks.empty() ){
          cout<<"\t\tcase 2: NEUTRAL DETECTION "
              <<caloEnergy<<" > "<<nsigma<<"x"<<TotalError
              <<" + "<<totalChargedMomentum<<endl;
          cout<<"\t\tneutral activity detected: "<<endl
              <<"\t\t\t           photon = "<<ePhoton<<endl
              <<"\t\t\tor neutral hadron = "<<eNeutralHadron<<endl;
            
          cout<<"\t\tphoton or hadron ?"<<endl;}
          
        if(sortedTracks.empty() )
          cout<<"\t\tno track -> hadron "<<endl;
        else 
          cout<<"\t\t"<<sortedTracks.size()
              <<"tracks -> check if the excess is photonic or hadronic"<<endl;
      }
        

      double ratioMax = 0.;
      reco::PFClusterRef maxEcalRef;
      unsigned maxiEcal= 9999;       

      // for each track associated to hcal: iterator IE ie :
        
      for(IE ie = sortedTracks.begin(); ie != sortedTracks.end(); ++ie ) {
          
        unsigned iTrack = ie->second;
          
        PFBlockElement::Type type = elements[iTrack].type();
        assert( type == reco::PFBlockElement::TRACK );
          
        reco::TrackRef trackRef = elements[iTrack].trackRef();
        assert( !trackRef.isNull() ); 
          
        II iae = associatedEcals.find(iTrack);
          
        if( iae == associatedEcals.end() ) continue;
          
        // double distECAL = iae->second.first;
        unsigned iEcal = iae->second.second;
          
        PFBlockElement::Type typeEcal = elements[iEcal].type();
        assert(typeEcal == reco::PFBlockElement::ECAL);
          
        reco::PFClusterRef clusterRef = elements[iEcal].clusterRef();
        assert( !clusterRef.isNull() );
          
        double pTrack = trackRef->p();
        double eECAL = clusterRef->energy();
        double eECALOverpTrack = eECAL / pTrack;

        if ( eECALOverpTrack > ratioMax ) { 
          ratioMax = eECALOverpTrack;
          maxEcalRef = clusterRef;
          maxiEcal = iEcal;
        }          
        
      }  // end loop on tracks associated to hcal: iterator IE ie
    
      std::vector<reco::PFClusterRef> pivotalClusterRef;
      std::vector<unsigned> iPivotal;
      std::vector<double> particleEnergy, ecalEnergy, hcalEnergy, rawecalEnergy, rawhcalEnergy;
      std::vector<math::XYZVector> particleDirection;

      // If the excess is smaller than the ecal energy, assign the whole 
      // excess to a photon
      if ( ePhoton < totalEcal || eNeutralHadron-calibEcal < 1E-10 ) { 
        
        if ( !maxEcalRef.isNull() ) { 
          particleEnergy.push_back(ePhoton);
          particleDirection.push_back(photonAtECAL);
          ecalEnergy.push_back(ePhoton);
          hcalEnergy.push_back(0.);
          rawecalEnergy.push_back(totalEcal);
          rawhcalEnergy.push_back(totalHcal);
          pivotalClusterRef.push_back(maxEcalRef);
          iPivotal.push_back(maxiEcal);
          // So the merged photon energy is,
          mergedPhotonEnergy  = ePhoton;
        }
        
      } else { 
        
        // Otherwise assign the whole ECAL energy to the photon
        if ( !maxEcalRef.isNull() ) { 
          pivotalClusterRef.push_back(maxEcalRef);
          particleEnergy.push_back(totalEcal);
          particleDirection.push_back(photonAtECAL);
          ecalEnergy.push_back(totalEcal);
          hcalEnergy.push_back(0.);
          rawecalEnergy.push_back(totalEcal);
          rawhcalEnergy.push_back(totalHcal);
          iPivotal.push_back(maxiEcal);
          // So the merged photon energy is,
          mergedPhotonEnergy  = totalEcal;
        }
        
        // ... and assign the remaining excess to a neutral hadron
        mergedNeutralHadronEnergy = eNeutralHadron-calibEcal;
        if ( mergedNeutralHadronEnergy > 1.0 ) { 
          pivotalClusterRef.push_back(hclusterref);
          particleEnergy.push_back(mergedNeutralHadronEnergy);
          particleDirection.push_back(hadronAtECAL);
          ecalEnergy.push_back(0.);
          hcalEnergy.push_back(mergedNeutralHadronEnergy);
          rawecalEnergy.push_back(totalEcal);
          rawhcalEnergy.push_back(totalHcal);
          iPivotal.push_back(iHcal);    
        }
        
      }
        

      // reconstructing a merged neutral
      // the type of PFCandidate is known from the 
      // reference to the pivotal cluster. 
      
      for ( unsigned iPivot=0; iPivot<iPivotal.size(); ++iPivot ) { 
        
        if ( particleEnergy[iPivot] < 0. ) 
          std::cout << "ALARM = Negative energy ! " 
                    << particleEnergy[iPivot] << std::endl;
        
        bool useDirection = true;
        unsigned tmpi = reconstructCluster( *pivotalClusterRef[iPivot], 
                                            particleEnergy[iPivot], 
                                            useDirection,
                                            particleDirection[iPivot].X(),
                                            particleDirection[iPivot].Y(),
                                            particleDirection[iPivot].Z()); 

      
        (*pfCandidates_)[tmpi].setEcalEnergy( rawecalEnergy[iPivot],ecalEnergy[iPivot] );
        if ( !useHO_ ) { 
          (*pfCandidates_)[tmpi].setHcalEnergy( rawhcalEnergy[iPivot],hcalEnergy[iPivot] );
          (*pfCandidates_)[tmpi].setHoEnergy(0., 0.);
        } else { 
          (*pfCandidates_)[tmpi].setHcalEnergy( rawhcalEnergy[iPivot]-totalHO,hcalEnergy[iPivot]*(1.-totalHO/rawhcalEnergy[iPivot]));
          (*pfCandidates_)[tmpi].setHoEnergy(totalHO, totalHO * hcalEnergy[iPivot]/rawhcalEnergy[iPivot]);
        } 
        (*pfCandidates_)[tmpi].setPs1Energy( 0. );
        (*pfCandidates_)[tmpi].setPs2Energy( 0. );
        (*pfCandidates_)[tmpi].set_mva_nothing_gamma( -1. );
        //       (*pfCandidates_)[tmpi].addElement(&elements[iPivotal]);
        // (*pfCandidates_)[tmpi].addElementInBlock(blockref, iPivotal[iPivot]);
        (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHcal );
        for ( unsigned ich=0; ich<chargedHadronsInBlock.size(); ++ich) { 
          unsigned iTrack = chargedHadronsInBlock[ich];
          (*pfCandidates_)[tmpi].addElementInBlock( blockref, iTrack );
          // Assign the position of the track at the ECAL entrance
          const math::XYZPointF& chargedPosition = 
            dynamic_cast<const reco::PFBlockElementTrack*>(&elements[iTrack])->positionAtECALEntrance();
          (*pfCandidates_)[tmpi].setPositionAtECALEntrance(chargedPosition);

          std::pair<II,II> myEcals = associatedEcals.equal_range(iTrack);
          for (II ii=myEcals.first; ii!=myEcals.second; ++ii ) { 
            unsigned iEcal = ii->second.second;
            if ( active[iEcal] ) continue;
            (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEcal );
          }
        }

      }

    } // excess of energy
  
  
    // will now share the hcal energy between the various charged hadron 
    // candidates, taking into account the potential neutral hadrons
    
    double totalHcalEnergyCalibrated = calibHcal;
    double totalEcalEnergyCalibrated = calibEcal;
    //JB: The question is: we've resolved the merged photons cleanly, but how should
    //the remaining hadrons be assigned the remaining ecal energy?
    //*Temporary solution*: follow HCAL example with fractions...    
 
   /*
    if(totalEcal>0) {
      // removing ecal energy from abc calibration
      totalHcalEnergyCalibrated  -= calibEcal;
      // totalHcalEnergyCalibrated -= calibration_->paramECALplusHCAL_slopeECAL() * totalEcal;
    }
    */
    // else caloEnergy = hcal only calibrated energy -> ok.

    // remove the energy of the potential neutral hadron
    totalHcalEnergyCalibrated -= mergedNeutralHadronEnergy;
    // similarly for the merged photons
    totalEcalEnergyCalibrated -= mergedPhotonEnergy;
    // share between the charged hadrons

    //COLIN can compute this before
    // not exactly equal to sum p, this is sum E
    double chargedHadronsTotalEnergy = 0;
    for( unsigned ich=0; ich<chargedHadronsIndices.size(); ++ich ) {
      unsigned index = chargedHadronsIndices[ich];
      reco::PFCandidate& chargedHadron = (*pfCandidates_)[index];
      chargedHadronsTotalEnergy += chargedHadron.energy();
    }

    for( unsigned ich=0; ich<chargedHadronsIndices.size(); ++ich ) {
      unsigned index = chargedHadronsIndices[ich];
      reco::PFCandidate& chargedHadron = (*pfCandidates_)[index];
      float fraction = chargedHadron.energy()/chargedHadronsTotalEnergy;

      if ( !useHO_ ) { 
        chargedHadron.setHcalEnergy(  fraction * totalHcal, fraction * totalHcalEnergyCalibrated );          
        chargedHadron.setHoEnergy(  0., 0. ); 
      } else { 
        chargedHadron.setHcalEnergy(  fraction * (totalHcal-totalHO), fraction * totalHcalEnergyCalibrated * (1.-totalHO/totalHcal) );          
        chargedHadron.setHoEnergy( fraction * totalHO, fraction * totalHO * totalHcalEnergyCalibrated / totalHcal ); 
      }
      //JB: fixing up (previously omitted) setting of ECAL energy gouzevit
      chargedHadron.setEcalEnergy( fraction * totalEcal, fraction * totalEcalEnergyCalibrated );
    }

    // Finally treat unused ecal satellites as photons.
    for ( IS is = ecalSatellites.begin(); is != ecalSatellites.end(); ++is ) { 
      
      // Ignore satellites already taken
      unsigned iEcal = is->second.first;
      if ( !active[iEcal] ) continue;

      // Sanity checks again (well not useful, this time!)
      PFBlockElement::Type type = elements[ iEcal ].type();
      assert( type == PFBlockElement::ECAL );
      PFClusterRef eclusterref = elements[iEcal].clusterRef();
      assert(!eclusterref.isNull() );

      // Lock the cluster
      active[iEcal] = false;
      
      // Find the associated tracks
      std::multimap<double, unsigned> assTracks;
      block.associatedElements( iEcal,  linkData,
                                assTracks,
                                reco::PFBlockElement::TRACK,
                                reco::PFBlock::LINKTEST_ALL );

      // Create a photon
      unsigned tmpi = reconstructCluster( *eclusterref, sqrt(is->second.second.Mag2()) ); 
      (*pfCandidates_)[tmpi].setEcalEnergy( eclusterref->energy(),sqrt(is->second.second.Mag2()) );
      (*pfCandidates_)[tmpi].setHcalEnergy( 0., 0. );
      (*pfCandidates_)[tmpi].setHoEnergy( 0., 0. );
      (*pfCandidates_)[tmpi].setPs1Energy( associatedPSs[iEcal].first );
      (*pfCandidates_)[tmpi].setPs2Energy( associatedPSs[iEcal].second );
      (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEcal );
      (*pfCandidates_)[tmpi].addElementInBlock( blockref, sortedTracks.begin()->second) ;
    }


  }

   // end loop on hcal element iHcal= hcalIs[i] 


  // Processing the remaining HCAL clusters
  if(debug_) { 
    cout<<endl;
    if(debug_) 
      cout<<endl
          <<"---- loop remaining hcal ------- "<<endl;
  }
  

  //COLINFEB16 
  // now dealing with the HCAL elements that are not linked to any track 
  for(unsigned ihcluster=0; ihcluster<hcalIs.size(); ihcluster++) {
    unsigned iHcal = hcalIs[ihcluster];

    // Keep ECAL and HO elements for reference in the PFCandidate
    std::vector<unsigned> ecalRefs;
    std::vector<unsigned> hoRefs;
    
    if(debug_)
      cout<<endl<<elements[iHcal]<<" ";
    
    
    if( !active[iHcal] ) {
      if(debug_) 
        cout<<"not active"<<endl;         
      continue;
    }
    
    // Find the ECAL elements linked to it
    std::multimap<double, unsigned> ecalElems;
    block.associatedElements( iHcal,  linkData,
                              ecalElems ,
                              reco::PFBlockElement::ECAL,
                              reco::PFBlock::LINKTEST_ALL );

    // Loop on these ECAL elements
    float totalEcal = 0.;
    float ecalMax = 0.;
    reco::PFClusterRef eClusterRef;
    for(IE ie = ecalElems.begin(); ie != ecalElems.end(); ++ie ) {
      
      unsigned iEcal = ie->second;
      double dist = ie->first;
      PFBlockElement::Type type = elements[iEcal].type();
      assert( type == PFBlockElement::ECAL );
      
      // Check if already used
      if( !active[iEcal] ) continue;

      // Check the distance (one HCALPlusECAL tower, roughly)
      // if ( dist > 0.15 ) continue;

      //COLINFEB16 
      // what could be done is to
      // - link by rechit.
      // - take in the neutral hadron all the ECAL clusters 
      // which are within the same CaloTower, according to the distance, 
      // except the ones which are closer to another HCAL cluster.
      // - all the other ECAL linked to this HCAL are photons. 
      // 
      // about the closest HCAL cluster. 
      // it could maybe be easier to loop on the ECAL clusters first
      // to cut the links to all HCAL clusters except the closest, as is 
      // done in the first track loop. But maybe not!
      // or add an helper function to the PFAlgo class to ask 
      // if a given element is the closest of a given type to another one?
    
      // Check if not closer from another free HCAL 
      std::multimap<double, unsigned> hcalElems;
      block.associatedElements( iEcal,  linkData,
                                hcalElems ,
                                reco::PFBlockElement::HCAL,
                                reco::PFBlock::LINKTEST_ALL );

      bool isClosest = true;
      for(IE ih = hcalElems.begin(); ih != hcalElems.end(); ++ih ) {

        unsigned jHcal = ih->second;
        double distH = ih->first;
        
        if ( !active[jHcal] ) continue;

        if ( distH < dist ) { 
          isClosest = false;
          break;
        }

      }

      if (!isClosest) continue;


      if(debug_) {
        cout<<"\telement "<<elements[iEcal]<<" linked with dist "<< dist<<endl;
        cout<<"Added to HCAL cluster to form a neutral hadron"<<endl;
      }
      
      reco::PFClusterRef eclusterRef = elements[iEcal].clusterRef();
      assert( !eclusterRef.isNull() );

      // Check the presence of ps clusters in the vicinity
      vector<double> ps1Ene(1,static_cast<double>(0.));
      associatePSClusters(iEcal, reco::PFBlockElement::PS1, block, elements, linkData, active, ps1Ene);
      vector<double> ps2Ene(1,static_cast<double>(0.));
      associatePSClusters(iEcal, reco::PFBlockElement::PS2, block, elements, linkData, active, ps2Ene);
      bool crackCorrection = false;
      double ecalEnergy = calibration_->energyEm(*eclusterRef,ps1Ene,ps2Ene,crackCorrection);
      
      //std::cout << "EcalEnergy, ps1, ps2 = " << ecalEnergy 
      //          << ", " << ps1Ene[0] << ", " << ps2Ene[0] << std::endl;
      totalEcal += ecalEnergy;
      if ( ecalEnergy > ecalMax ) { 
        ecalMax = ecalEnergy;
        eClusterRef = eclusterRef;
      }
      
      ecalRefs.push_back(iEcal);
      active[iEcal] = false;


    }// End loop ECAL
    
    // Now find the HO clusters linked to the HCAL cluster
    double totalHO = 0.;
    double hoMax = 0.;
    //unsigned jHO = 0;
    if (useHO_) {
      std::multimap<double, unsigned> hoElems;
      block.associatedElements( iHcal,  linkData,
                                hoElems ,
                                reco::PFBlockElement::HO,
                                reco::PFBlock::LINKTEST_ALL );
      
      // Loop on these HO elements
      //      double totalHO = 0.;
      //      double hoMax = 0.;
      //      unsigned jHO = 0;
      reco::PFClusterRef hoClusterRef;
      for(IE ie = hoElems.begin(); ie != hoElems.end(); ++ie ) {
        
        unsigned iHO = ie->second;
        double dist = ie->first;
        PFBlockElement::Type type = elements[iHO].type();
        assert( type == PFBlockElement::HO );
        
        // Check if already used
        if( !active[iHO] ) continue;
        
        // Check the distance (one HCALPlusHO tower, roughly)
        // if ( dist > 0.15 ) continue;
        
        // Check if not closer from another free HCAL 
        std::multimap<double, unsigned> hcalElems;
        block.associatedElements( iHO,  linkData,
                                  hcalElems ,
                                  reco::PFBlockElement::HCAL,
                                  reco::PFBlock::LINKTEST_ALL );
        
        bool isClosest = true;
        for(IE ih = hcalElems.begin(); ih != hcalElems.end(); ++ih ) {
          
          unsigned jHcal = ih->second;
          double distH = ih->first;
          
          if ( !active[jHcal] ) continue;
          
          if ( distH < dist ) { 
            isClosest = false;
            break;
          }
          
        }
        
        if (!isClosest) continue;
        
        if(debug_ && useHO_) {
          cout<<"\telement "<<elements[iHO]<<" linked with dist "<< dist<<endl;
          cout<<"Added to HCAL cluster to form a neutral hadron"<<endl;
        }
        
        reco::PFClusterRef hoclusterRef = elements[iHO].clusterRef();
        assert( !hoclusterRef.isNull() );
        
        double hoEnergy = hoclusterRef->energy(); // calibration_->energyEm(*hoclusterRef,ps1Ene,ps2Ene,crackCorrection);
        
        totalHO += hoEnergy;
        if ( hoEnergy > hoMax ) { 
          hoMax = hoEnergy;
          hoClusterRef = hoclusterRef;
          //jHO = iHO;
        }
        
        hoRefs.push_back(iHO);
        active[iHO] = false;
        
      }// End loop HO
    }

    PFClusterRef hclusterRef 
      = elements[iHcal].clusterRef();
    assert( !hclusterRef.isNull() );
    
    // HCAL energy
    double totalHcal =  hclusterRef->energy();
    // Include the HO energy 
    if ( useHO_ ) totalHcal += totalHO;

    // std::cout << "Hcal Energy,eta : " << totalHcal 
    //          << ", " << hclusterRef->positionREP().Eta()
    //          << std::endl;
    // Calibration
    //double caloEnergy = totalHcal;
    // double slopeEcal = 1.0;
    double calibEcal = totalEcal > 0. ? totalEcal : 0.;
    double calibHcal = std::max(0.,totalHcal);
    if  (  hclusterRef->layer() == PFLayer::HF_HAD  ||
           hclusterRef->layer() == PFLayer::HF_EM ) { 
      //caloEnergy = totalHcal/0.7;
      calibEcal = totalEcal;
    } else { 
      calibration_->energyEmHad(-1.,calibEcal,calibHcal,
                                hclusterRef->positionREP().Eta(),
                                hclusterRef->positionREP().Phi());      
      //caloEnergy = calibEcal+calibHcal;
    }

    // std::cout << "CalibEcal,HCal = " << calibEcal << ", " << calibHcal << std::endl;
    // std::cout << "-------------------------------------------------------------------" << std::endl;
    // ECAL energy : calibration

    // double particleEnergy = caloEnergy;
    // double particleEnergy = totalEcal + calibHcal;
    // particleEnergy /= (1.-0.724/sqrt(particleEnergy)-0.0226/particleEnergy);

    unsigned tmpi = reconstructCluster( *hclusterRef, 
                                        calibEcal+calibHcal ); 

    
    (*pfCandidates_)[tmpi].setEcalEnergy( totalEcal, calibEcal );
    if ( !useHO_ ) { 
      (*pfCandidates_)[tmpi].setHcalEnergy( totalHcal, calibHcal );
      (*pfCandidates_)[tmpi].setHoEnergy(0.,0.);
    } else { 
      (*pfCandidates_)[tmpi].setHcalEnergy( totalHcal-totalHO, calibHcal*(1.-totalHO/totalHcal));
      (*pfCandidates_)[tmpi].setHoEnergy(totalHO,totalHO*calibHcal/totalHcal);
    }
    (*pfCandidates_)[tmpi].setPs1Energy( 0. );
    (*pfCandidates_)[tmpi].setPs2Energy( 0. );
    (*pfCandidates_)[tmpi].addElementInBlock( blockref, iHcal );
    for (unsigned iec=0; iec<ecalRefs.size(); ++iec) 
      (*pfCandidates_)[tmpi].addElementInBlock( blockref, ecalRefs[iec] );
    for (unsigned iho=0; iho<hoRefs.size(); ++iho) 
      (*pfCandidates_)[tmpi].addElementInBlock( blockref, hoRefs[iho] );
      
  }//loop hcal elements




  if(debug_) { 
    cout<<endl;
    if(debug_) cout<<endl<<"---- loop ecal------- "<<endl;
  }
  
  // for each ecal element iEcal = ecalIs[i] in turn:

  for(unsigned i=0; i<ecalIs.size(); i++) {
    unsigned iEcal = ecalIs[i];
    
    if(debug_) 
      cout<<endl<<elements[iEcal]<<" ";
    
    if( ! active[iEcal] ) {
      if(debug_) 
        cout<<"not active"<<endl;         
      continue;
    }
    
    PFBlockElement::Type type = elements[ iEcal ].type();
    assert( type == PFBlockElement::ECAL );

    PFClusterRef clusterref = elements[iEcal].clusterRef();
    assert(!clusterref.isNull() );

    active[iEcal] = false;

    // Check the presence of ps clusters in the vicinity
    vector<double> ps1Ene(1,static_cast<double>(0.));
    associatePSClusters(iEcal, reco::PFBlockElement::PS1, block, elements, linkData, active, ps1Ene);
    vector<double> ps2Ene(1,static_cast<double>(0.));
    associatePSClusters(iEcal, reco::PFBlockElement::PS2, block, elements, linkData, active, ps2Ene);
    bool crackCorrection = false;
    float ecalEnergy = calibration_->energyEm(*clusterref,ps1Ene,ps2Ene,crackCorrection);
    // float ecalEnergy = calibration_->energyEm( clusterref->energy() );
    double particleEnergy = ecalEnergy;
    
    unsigned tmpi = reconstructCluster( *clusterref, 
                                        particleEnergy );
 
    (*pfCandidates_)[tmpi].setEcalEnergy( clusterref->energy(),ecalEnergy );
    (*pfCandidates_)[tmpi].setHcalEnergy( 0., 0. );
    (*pfCandidates_)[tmpi].setHoEnergy( 0., 0. );
    (*pfCandidates_)[tmpi].setPs1Energy( 0. );
    (*pfCandidates_)[tmpi].setPs2Energy( 0. );
    (*pfCandidates_)[tmpi].addElementInBlock( blockref, iEcal );
    

  }  // end loop on ecal elements iEcal = ecalIs[i]

}  // end processBlock