/* * Delphes: a framework for fast simulation of a generic collider experiment * Copyright (C) 2012-2014 Universite catholique de Louvain (UCL), Belgium * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY || FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** \class JetFlavorAssociation * * Find origin of jet && evaluate jet flavor * * \author P. Demin - UCL, Louvain-la-Neuve * */ #include "modules/JetFlavorAssociation.h" #include "classes/DelphesClasses.h" #include "classes/DelphesFactory.h" #include "classes/DelphesFormula.h" #include "ExRootAnalysis/ExRootResult.h" #include "ExRootAnalysis/ExRootFilter.h" #include "ExRootAnalysis/ExRootClassifier.h" #include "TMath.h" #include "TString.h" #include "TFormula.h" #include "TRandom3.h" #include "TObjArray.h" #include "TDatabasePDG.h" #include "TLorentzVector.h" #include #include #include #include using namespace std; //------------------------------------------------------------------------------ class PartonClassifier: public ExRootClassifier { public: PartonClassifier() {} Int_t GetCategory(TObject *object); Double_t fEtaMax, fPTMin; }; //------------------------------------------------------------------------------ // https://cmssdt.cern.ch/SDT/lxr/source/PhysicsTools/JetMCAlgos/plugins/PartonSelector.cc Int_t PartonClassifier::GetCategory(TObject *object) { // select parton in the parton list Candidate *parton = static_cast(object); const TLorentzVector &momentum = parton->Momentum; Int_t pdgCode; // inside the eta && momentum range (be a little bit larger that the tracking coverage if(momentum.Pt() <= fPTMin || TMath::Abs(momentum.Eta()) > fEtaMax) return -1; pdgCode = TMath::Abs(parton->PID); if(parton->Status == -1) return -1; if(pdgCode != 21 && pdgCode > 5) return -1; // not a parton, skip if(parton->Status == 3 || parton->Status == 2) return 0; // if status 3 return return 0; } //------------------------------------------------------------------------------ class PartonClassifierLHEF: public ExRootClassifier { public: PartonClassifierLHEF() {} Int_t GetCategory(TObject *object); Double_t fEtaMax, fPTMin; }; Int_t PartonClassifierLHEF::GetCategory(TObject *object) { // select parton in the parton list Candidate *parton = static_cast(object); const TLorentzVector &momentum = parton->Momentum; Int_t pdgCode; // inside the eta && momentum range (be a little bit larger that the tracking coverage if(momentum.Pt() <= fPTMin || TMath::Abs(momentum.Eta()) > fEtaMax) return -1; pdgCode = TMath::Abs(parton->PID); if(parton->Status == -1) return -1; if(pdgCode != 21 && pdgCode > 5) return -1; // not a parton, skip if(parton->Status != 1) return -1; // if status 3 return return 0; } //------------------------------------------------------------------------------ JetFlavorAssociation::JetFlavorAssociation() : fClassifier(0), fFilter(0), fItPartonInputArray(0), fItPartonInputArrayLHEF(0), fItJetInputArray(0), fItParticleInputArray(0) { fClassifier = new PartonClassifier; fClassifierLHEF = new PartonClassifierLHEF; } //------------------------------------------------------------------------------ JetFlavorAssociation::~JetFlavorAssociation() { if(fClassifier) delete fClassifier; if(fClassifierLHEF) delete fClassifierLHEF; } //------------------------------------------------------------------------------ void JetFlavorAssociation::Init() { ExRootConfParam param; fDeltaR = GetDouble("DeltaR", 0.5); fClassifier->fPTMin = GetDouble("PartonPTMin", 0.); fClassifier->fEtaMax = GetDouble("PartonEtaMax",2.5); fClassifierLHEF->fPTMin = GetDouble("PartonPTMin", 0.); fClassifierLHEF->fEtaMax = GetDouble("PartonEtaMax",2.5); // import input array(s) fPartonInputArray = ImportArray(GetString("PartonInputArray", "Delphes/partons")); fItPartonInputArray = fPartonInputArray->MakeIterator(); fPartonInputArrayLHEF = ImportArray(GetString("PartonInputArrayLHEF", "Delphes/partonsLHEF")); fItPartonInputArrayLHEF = fPartonInputArrayLHEF->MakeIterator(); fFilter = new ExRootFilter(fPartonInputArray); fFilterLHEF = new ExRootFilter(fPartonInputArrayLHEF); fJetInputArray = ImportArray(GetString("JetInputArray", "FastJetFinder/jets")); fItJetInputArray = fJetInputArray->MakeIterator(); fParticleInputArray = ImportArray(GetString("ParticleInputArray", "Delphes/allParticles")); fItParticleInputArray = fParticleInputArray->MakeIterator(); } //------------------------------------------------------------------------------ void JetFlavorAssociation::Finish() { if(fFilter) delete fFilter; if(fFilterLHEF) delete fFilterLHEF; if(fItJetInputArray) delete fItJetInputArray; if(fItParticleInputArray) delete fItParticleInputArray; if(fItPartonInputArray) delete fItPartonInputArray; if(fItPartonInputArrayLHEF) delete fItPartonInputArrayLHEF; } //------------------------------------------------------------------------------ void JetFlavorAssociation::Process(){ Candidate *jet; TObjArray *partonArray; TObjArray *partonArrayLHEF; // select quark && gluons fFilter->Reset(); partonArray = fFilter->GetSubArray(fClassifier, 0); // get the filtered parton array if(partonArray == 0) return; TIter itPartonArray(partonArray); fFilterLHEF->Reset(); partonArrayLHEF = fFilterLHEF->GetSubArray(fClassifierLHEF, 0); // get the filtered parton array if(partonArrayLHEF == 0) return; TIter itPartonLHEFArray(partonArrayLHEF); // loop over all input jets fItJetInputArray->Reset(); while((jet = static_cast(fItJetInputArray->Next()))) { // get standard flavor GetAlgoFlavor(jet, itPartonArray, itPartonLHEFArray); GetPhysicsFlavor(jet, itPartonArray, itPartonLHEFArray); } } //------------------------------------------------------------------------------ // Standard definition of jet flavor in // https://cmssdt.cern.ch/SDT/lxr/source/PhysicsTools/JetMCAlgos/plugins/JetPartonMatcher.cc?v=CMSSW_7_3_0_pre1 void JetFlavorAssociation::GetAlgoFlavor(Candidate *jet, TIter &itPartonArray, TIter &itPartonLHEFArray) { float maxPt = 0; float minDr = 1000; bool isGoodParton = true; int daughterCounter = 0; Candidate *parton, *partonLHEF; Candidate *tempParton = 0, *tempPartonNearest = 0, *tempPartonHighestPt = 0; int pdgCode, pdgCodeMax = -1; itPartonArray.Reset(); while((parton = static_cast(itPartonArray.Next()))) { // default delphes method pdgCode = TMath::Abs(parton->PID); if(TMath::Abs(parton->PID) == 21) pdgCode = 0; if(jet->Momentum.DeltaR(parton->Momentum) <= fDeltaR) { if(pdgCodeMax < pdgCode) pdgCodeMax = pdgCode; } isGoodParton = true; itPartonLHEFArray.Reset(); while((partonLHEF = static_cast(itPartonLHEFArray.Next()))) { if(parton->Momentum.DeltaR(partonLHEF->Momentum) < 0.001 && parton->PID == partonLHEF->PID && partonLHEF->Charge == parton->Charge) { isGoodParton = false; break; } if(!isGoodParton) continue; // check the daugheter daughterCounter = 0; if(parton->D1 != -1 || parton->D2 != -1) { // partons are only quarks || gluons int daughterFlavor1 = -1; int daughterFlavor2 = -1; if(parton->D1 != -1) daughterFlavor1 = TMath::Abs(static_cast(fParticleInputArray->At(parton->D1))->PID); if(parton->D2 != -1) daughterFlavor2 = TMath::Abs(static_cast(fParticleInputArray->At(parton->D2))->PID); if((daughterFlavor1 == 1 || daughterFlavor1 == 2 || daughterFlavor1 == 3 || daughterFlavor1 == 4 || daughterFlavor1 == 5 || daughterFlavor1 == 21)) daughterCounter++; if((daughterFlavor2 == 1 || daughterFlavor2 == 2 || daughterFlavor2 == 3 || daughterFlavor2 == 4 || daughterFlavor1 == 5 || daughterFlavor2 == 21)) daughterCounter++; } if(daughterCounter > 0) continue; if(jet->Momentum.DeltaR(parton->Momentum) <= fDeltaR) { if(jet->Momentum.DeltaR(parton->Momentum) < minDr) { minDr = jet->Momentum.DeltaR(parton->Momentum); tempPartonNearest = parton; } // if not yet found && pdgId is a c, take as c if(TMath::Abs(parton->PID) == 4) tempParton = parton; if(TMath::Abs(parton->PID) == 5) tempParton = parton; if(parton->Momentum.Pt() > maxPt) { maxPt = parton->Momentum.Pt(); tempPartonHighestPt = parton; } } } } jet->FlavorHeaviest = tempParton ? TMath::Abs(tempParton->PID) : 0; jet->FlavorHighestPt = tempPartonHighestPt ? TMath::Abs(tempPartonHighestPt->PID) : 0; jet->FlavorNearest2 = tempPartonNearest ? TMath::Abs(tempPartonNearest->PID) : 0; if(!tempParton) tempParton = tempPartonHighestPt; jet->FlavorAlgo = tempParton ? TMath::Abs(tempParton->PID) : 0; if(pdgCodeMax == 0) pdgCodeMax = 21; if(pdgCodeMax == -1) pdgCodeMax = 0; jet->FlavorDefault = pdgCodeMax; } //------------------------------------------------------------------------------ void JetFlavorAssociation::GetPhysicsFlavor(Candidate *jet, TIter &itPartonArray, TIter &itPartonLHEFArray) { float minDr = 1000; int partonCounter = 0; float biggerConeSize = 0.7; float dist; bool isGoodCandidate; int contaminatingFlavor = 0; int motherCounter = 0; Candidate *parton, *partonLHEF, *mother1, *mother2; Candidate *tempParton = 0, *tempPartonNearest = 0; vector contaminations; vector::iterator itContaminations; contaminations.clear(); itPartonLHEFArray.Reset(); while((partonLHEF = static_cast(itPartonLHEFArray.Next()))) { dist = jet->Momentum.DeltaR(partonLHEF->Momentum); // take the DR if(partonLHEF->Status == 1 && dist < minDr) { tempPartonNearest = partonLHEF; minDr = dist; } if(partonLHEF->Status == 1 && dist <= fDeltaR) { tempParton = partonLHEF; partonCounter++; } } itPartonArray.Reset(); itPartonLHEFArray.Reset(); while((parton = static_cast(itPartonArray.Next()))) { dist = jet->Momentum.DeltaR(parton->Momentum); // take the DR isGoodCandidate = true; while((partonLHEF = static_cast(itPartonLHEFArray.Next()))) { if(parton->Momentum.DeltaR(partonLHEF->Momentum) < 0.01 && parton->PID == partonLHEF->PID && partonLHEF->Charge == parton->Charge) { isGoodCandidate = false; break; } } if(!isGoodCandidate) continue; if(parton->D1 != -1 || parton->D2 != -1) { if((TMath::Abs(parton->PID) < 4 || TMath::Abs(parton->PID) == 21)) continue; if(dist < biggerConeSize) contaminations.push_back(parton); } } jet->FlavorNearest3 = tempPartonNearest ? TMath::Abs(tempPartonNearest->PID) : 0; if(partonCounter != 1) { jet->FlavorPhysics = 0; } else if(contaminations.size() == 0) { jet->FlavorPhysics = TMath::Abs(tempParton->PID); } else if(contaminations.size() > 0) { jet->FlavorPhysics = TMath::Abs(tempParton->PID); for(itContaminations = contaminations.begin(); itContaminations != contaminations.end(); ++itContaminations) { parton = *itContaminations; contaminatingFlavor = TMath::Abs(parton->PID); motherCounter = 0; if(parton->M1 != -1) motherCounter++; if(parton->M2 != -1) motherCounter++; if(parton->M1 != -1) { mother1 = static_cast(fParticleInputArray->At(parton->M1)); if(mother1 && motherCounter > 0 && mother1->Momentum.DeltaR(tempParton->Momentum) < 0.001) continue; } if(parton->M2 != -1) { mother2 = static_cast(fParticleInputArray->At(parton->M2)); if(mother2 && motherCounter > 0 && mother2->Momentum.DeltaR(tempParton->Momentum) < 0.001) continue; } // mother is the initialParton --> OK if(TMath::Abs(tempParton->PID) == 4) { // keep association --> the initialParton is a c --> the contaminated parton is a c if(contaminatingFlavor == 4) continue; jet->FlavorPhysics = 0; // all the other cases reject! break; } } } }