LeptonFSRProducer

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
/** \class LeptonFSRProducer
 * Search for FSR photons for muons and electrons.
 *
 * Photon candidates are searched among the "packedPFCandidates" collection with the specified cuts, and are required to be isolated 
 * (relIso, with a cone of 0.3) and not to be in the footprint of all electrons in the "electrons" collection.
 * Each photon is matched by DeltaR to the closest among all muons and electrons and stored if passing dR/ET^2<deltaROverEt2Max.
 * In addition ValueMaps are stored, with links to one photon per muon/electron. For this purpose, if more than a photon
 * is matched to a lepton, the lowest-DR/ET^2 is chosen.
 *
 */

#include <memory>

#include "DataFormats/Candidate/interface/Candidate.h"
#include "DataFormats/Common/interface/ValueMap.h"
#include "DataFormats/Math/interface/LorentzVector.h"
#include "DataFormats/PatCandidates/interface/Electron.h"
#include "DataFormats/PatCandidates/interface/GenericParticle.h"
#include "DataFormats/PatCandidates/interface/Muon.h"
#include "DataFormats/PatCandidates/interface/PackedCandidate.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/MakerMacros.h"
#include "FWCore/Framework/interface/global/EDProducer.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/Utilities/interface/StreamID.h"

class LeptonFSRProducer : public edm::global::EDProducer<> {
public:
  explicit LeptonFSRProducer(const edm::ParameterSet& iConfig)
      : pfcands_{consumes<pat::PackedCandidateCollection>(iConfig.getParameter<edm::InputTag>("packedPFCandidates"))},
        electronsForVeto_{consumes<pat::ElectronCollection>(iConfig.getParameter<edm::InputTag>("slimmedElectrons"))},
        muons_{consumes<edm::View<reco::Muon>>(iConfig.getParameter<edm::InputTag>("muons"))},
        electrons_{consumes<edm::View<reco::GsfElectron>>(iConfig.getParameter<edm::InputTag>("electrons"))},
        ptCutMu(iConfig.getParameter<double>("muonPtMin")),
        etaCutMu(iConfig.getParameter<double>("muonEtaMax")),
        ptCutE(iConfig.getParameter<double>("elePtMin")),
        etaCutE(iConfig.getParameter<double>("eleEtaMax")),
        photonPtCut(iConfig.getParameter<double>("photonPtMin")),
        drEtCut(iConfig.getParameter<double>("deltaROverEt2Max")),
        isoCut(iConfig.getParameter<double>("isolation")),
        drSafe(0.0001) {
    produces<std::vector<pat::GenericParticle>>();
    produces<edm::ValueMap<int>>("muFsrIndex");
    produces<edm::ValueMap<int>>("eleFsrIndex");
  }
  static void fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
    edm::ParameterSetDescription desc;
    desc.add<edm::InputTag>("packedPFCandidates", edm::InputTag("packedPFCandidates"))
        ->setComment("packed pf candidates where to look for photons");
    desc.add<edm::InputTag>("slimmedElectrons", edm::InputTag("slimmedElectrons"))
        ->setComment(
            "electrons to check for footprint, the electron collection must have proper linking with the "
            "packedCandidate collection");
    desc.add<edm::InputTag>("muons", edm::InputTag("slimmedMuons"))
        ->setComment("collection of muons to match with FSR ");
    desc.add<edm::InputTag>("electrons", edm::InputTag("slimmedElectrons"))
        ->setComment("collection of electrons to match with FSR ");
    desc.add<double>("muonPtMin", 3.)->setComment("minimum pt of the muon to look for a near photon");
    desc.add<double>("muonEtaMax", 2.4)->setComment("max eta of the muon to look for a near photon");
    desc.add<double>("elePtMin", 5.)->setComment("minimum pt of the electron to look for a near photon");
    desc.add<double>("eleEtaMax", 2.5)->setComment("max eta of the electron to look for a near photon");
    desc.add<double>("photonPtMin", 2.0)->setComment("minimum photon Pt");
    desc.add<double>("deltaROverEt2Max", 0.05)->setComment("max ratio of deltaR(lep,photon) over et2 of the photon");
    desc.add<double>("isolation", 2.0)->setComment("photon relative isolation cut");

    descriptions.addWithDefaultLabel(desc);
  }
  ~LeptonFSRProducer() override = default;

private:
  void produce(edm::StreamID, edm::Event&, const edm::EventSetup&) const override;

  double computeRelativeIsolation(const pat::PackedCandidate& photon,
                                  const pat::PackedCandidateCollection& pfcands,
                                  const double& isoConeMax2,
                                  const double& isoConeMin2) const;

  bool electronFootprintVeto(pat::PackedCandidateRef& pfcandRef,
                             edm::Handle<pat::ElectronCollection> electronsForVeto) const;

  // ----------member data ---------------------------
  const edm::EDGetTokenT<pat::PackedCandidateCollection> pfcands_;
  const edm::EDGetTokenT<pat::ElectronCollection> electronsForVeto_;
  const edm::EDGetTokenT<edm::View<reco::Muon>> muons_;
  const edm::EDGetTokenT<edm::View<reco::GsfElectron>> electrons_;
  const double ptCutMu;
  const double etaCutMu;
  const double ptCutE;
  const double etaCutE;
  const double photonPtCut;
  const double drEtCut;
  const double isoCut;
  const double drSafe;
};

void LeptonFSRProducer::produce(edm::StreamID streamID, edm::Event& iEvent, const edm::EventSetup& iSetup) const {
  using namespace std;

  edm::Handle<pat::PackedCandidateCollection> pfcands;
  iEvent.getByToken(pfcands_, pfcands);
  edm::Handle<edm::View<reco::Muon>> muons;
  iEvent.getByToken(muons_, muons);
  edm::Handle<edm::View<reco::GsfElectron>> electrons;
  iEvent.getByToken(electrons_, electrons);
  edm::Handle<pat::ElectronCollection> electronsForVeto;
  iEvent.getByToken(electronsForVeto_, electronsForVeto);

  // The output collection of FSR photons
  auto fsrPhotons = std::make_unique<std::vector<pat::GenericParticle>>();

  std::vector<int> muPhotonIdxs(muons->size(), -1);
  std::vector<double> muPhotonDRET2(muons->size(), 1e9);

  std::vector<int> elePhotonIdxs(electrons->size(), -1);
  std::vector<double> elePhotonDRET2(electrons->size(), 1e9);

  //----------------------
  // Loop on photon candidates
  //----------------------

  for (auto pc = pfcands->begin(); pc != pfcands->end(); pc++) {
    // consider only photons, with pT and eta cuts
    if (abs(pc->pdgId()) != 22 || pc->pt() < photonPtCut || abs(pc->eta()) > 2.5)
      continue;

    //------------------------------------------------------
    // Get the closest lepton
    //------------------------------------------------------
    double dRMin(0.5);
    int closestMu = -1;
    int closestEle = -1;
    double photon_relIso03 = 1e9;  // computed only if necessary
    bool skipPhoton = false;

    for (auto muon = muons->begin(); muon != muons->end(); ++muon) {
      if (muon->pt() < ptCutMu || std::abs(muon->eta()) > etaCutMu)
        continue;

      int muonIdx = muon - muons->begin();
      double dR = deltaR(muon->eta(), muon->phi(), pc->eta(), pc->phi());
      if (dR < dRMin && dR > drSafe && dR < drEtCut * pc->pt() * pc->pt()) {
        // Check if photon is isolated
        photon_relIso03 = computeRelativeIsolation(*pc, *pfcands, 0.3 * 0.3, drSafe * drSafe);
        if (photon_relIso03 > isoCut) {
          skipPhoton = true;
          break;  // break loop on muons -> photon will be skipped
        }
        // Check that photon is not in footprint of an electron
        pat::PackedCandidateRef pfcandRef = pat::PackedCandidateRef(pfcands, pc - pfcands->begin());
        skipPhoton = electronFootprintVeto(pfcandRef, electronsForVeto);
        if (skipPhoton)
          break;  // break loop on muons -> photon will be skipped

        // Candidate matching
        dRMin = dR;
        closestMu = muonIdx;
      }
    }  // end of loop on muons

    if (skipPhoton)
      continue;  // photon does not pass iso or ele footprint veto; do not look for electrons

    for (auto ele = electrons->begin(); ele != electrons->end(); ++ele) {
      if (ele->pt() < ptCutE || std::abs(ele->eta()) > etaCutE)
        continue;

      int eleIdx = ele - electrons->begin();
      double dR = deltaR(ele->eta(), ele->phi(), pc->eta(), pc->phi());
      if (dR < dRMin && dR > drSafe && dR < drEtCut * pc->pt() * pc->pt()) {
        // Check if photon is isolated (no need to recompute iso if already done for muons above)
        if (photon_relIso03 > 1e8) {
          photon_relIso03 = computeRelativeIsolation(*pc, *pfcands, 0.3 * 0.3, drSafe * drSafe);
        }
        if (photon_relIso03 > isoCut) {
          break;  // break loop on electrons -> photon will be skipped
        }
        // Check that photon is not in footprint of an electron
        pat::PackedCandidateRef pfcandRef = pat::PackedCandidateRef(pfcands, pc - pfcands->begin());
        if (electronFootprintVeto(pfcandRef, electronsForVeto)) {
          break;  // break loop on electrons -> photon will be skipped
        }

        // Candidate matching
        dRMin = dR;
        closestEle = eleIdx;
        closestMu = -1;  // reset match to muons
      }
    }  // end loop on electrons

    if (closestMu >= 0 || closestEle >= 0) {
      // Add FSR photon to the output collection
      double dRET2 = dRMin / pc->pt() / pc->pt();
      int iPhoton = fsrPhotons->size();
      fsrPhotons->push_back(pat::GenericParticle(*pc));
      fsrPhotons->back().addUserFloat("relIso03", photon_relIso03);
      fsrPhotons->back().addUserFloat("dROverEt2", dRET2);

      if (closestMu >= 0) {
        fsrPhotons->back().addUserCand("associatedMuon", reco::CandidatePtr(muons, closestMu));
        // Store the backlink to the photon: choose the lowest-dRET2 photon for each mu...
        if (dRET2 < muPhotonDRET2[closestMu]) {
          muPhotonDRET2[closestMu] = dRET2;
          muPhotonIdxs[closestMu] = iPhoton;
        }
      } else if (closestEle >= 0) {
        // ...and same for eles
        fsrPhotons->back().addUserCand("associatedElectron", reco::CandidatePtr(electrons, closestEle));
        if (dRET2 < elePhotonDRET2[closestEle]) {
          elePhotonDRET2[closestEle] = dRET2;
          elePhotonIdxs[closestEle] = iPhoton;
        }
      }
    }
  }  // end of loop over pfCands

  iEvent.put(std::move(fsrPhotons));

  {
    std::unique_ptr<edm::ValueMap<int>> bareIdx(new edm::ValueMap<int>());
    edm::ValueMap<int>::Filler fillerBareIdx(*bareIdx);
    fillerBareIdx.insert(muons, muPhotonIdxs.begin(), muPhotonIdxs.end());
    fillerBareIdx.fill();
    iEvent.put(std::move(bareIdx), "muFsrIndex");
  }

  {
    std::unique_ptr<edm::ValueMap<int>> bareIdx(new edm::ValueMap<int>());
    edm::ValueMap<int>::Filler fillerBareIdx(*bareIdx);
    fillerBareIdx.insert(electrons, elePhotonIdxs.begin(), elePhotonIdxs.end());
    fillerBareIdx.fill();
    iEvent.put(std::move(bareIdx), "eleFsrIndex");
  }
}

double LeptonFSRProducer::computeRelativeIsolation(const pat::PackedCandidate& photon,
                                                   const pat::PackedCandidateCollection& pfcands,
                                                   const double& isoConeMax2,
                                                   const double& isoConeMin2) const {
  double ptsum = 0;

  for (const auto& pfcand : pfcands) {
    // Isolation cone
    double dRIsoCone2 = deltaR2(photon.eta(), photon.phi(), pfcand.eta(), pfcand.phi());
    if (dRIsoCone2 > isoConeMax2 || dRIsoCone2 < isoConeMin2)
      continue;

    // Charged hadrons
    if (pfcand.charge() != 0 && abs(pfcand.pdgId()) == 211 && pfcand.pt() > 0.2 && dRIsoCone2 > drSafe * drSafe) {
      ptsum += pfcand.pt();
      // Neutral hadrons + photons
    } else if (pfcand.charge() == 0 && (abs(pfcand.pdgId()) == 22 || abs(pfcand.pdgId()) == 130) && pfcand.pt() > 0.5 &&
               dRIsoCone2 > 0.01 * 0.01) {
      ptsum += pfcand.pt();
    }
  }

  return ptsum / photon.pt();
}

bool LeptonFSRProducer::electronFootprintVeto(pat::PackedCandidateRef& pfcandRef,
                                              edm::Handle<pat::ElectronCollection> electronsForVeto) const {
  bool skipPhoton = false;
  for (auto electrons_iter = electronsForVeto->begin(); electrons_iter != electronsForVeto->end(); ++electrons_iter) {
    for (auto const& cand : electrons_iter->associatedPackedPFCandidates()) {
      if (!cand.isAvailable())
        continue;
      if (cand.id() != pfcandRef.id())
        throw cms::Exception("Configuration")
            << "The electron associatedPackedPFCandidates item does not have "
            << "the same ID of packed candidate collection used for cleaning the electron footprint: " << cand.id()
            << " (" << pfcandRef.id() << ")\n";
      if (cand.key() == pfcandRef.key()) {
        skipPhoton = true;
        break;
      }
    }
  }
  return skipPhoton;
}

//define this as a plug-in
DEFINE_FWK_MODULE(LeptonFSRProducer);