示例#1
0
void selectWe(const TString conf,        // input file
              const TString outputDir,   // output directory
	      const Bool_t  doScaleCorr  // apply energy scale corrections?
) {
  gBenchmark->Start("selectWe");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t PT_CUT   = 20;
  const Double_t ETA_CUT  = 2.5;
  const Double_t ELE_MASS = 0.000511;
  
  const Double_t ECAL_GAP_LOW  = 1.4442;
  const Double_t ECAL_GAP_HIGH = 1.566;
  
  const Double_t escaleNbins  = 6;
  const Double_t escaleEta[]  = { 0.4,     0.8,     1.2,     1.4442,  2,        2.5 };
  const Double_t escaleCorr[] = { 1.00284, 1.00479, 1.00734, 1.00851, 1.00001,  0.982898 };
  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  vector<TString>  snamev;      // sample name (for output files)  
  vector<CSample*> samplev;     // data/MC samples

  //
  // parse .conf file
  //
  confParse(conf, snamev, samplev);
  const Bool_t hasData = (samplev[0]->fnamev.size()>0);

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  const TString ntupDir = outputDir + TString("/ntuples");
  gSystem->mkdir(ntupDir,kTRUE);
  
  //
  // Declare output ntuple variables
  //
  UInt_t  runNum, lumiSec, evtNum;
  UInt_t  npv, npu;
  Float_t genVPt, genVPhi, genVy, genVMass;
  Float_t genLepPt, genLepPhi;
  Float_t scale1fb;
  Float_t met, metPhi, sumEt, mt, u1, u2;
  Int_t   q;
  LorentzVector *lep=0;
  ///// electron specific /////
  Float_t trkIso, emIso, hadIso;
  Float_t pfChIso, pfGamIso, pfNeuIso, pfCombIso;
  Float_t sigieie, hovere, eoverp, fbrem, ecalE;
  Float_t dphi, deta;
  Float_t d0, dz;
  UInt_t  isConv, nexphits, typeBits;
  LorentzVector *sc=0;
  
  // Data structures to store info from TTrees
  mithep::TEventInfo *info  = new mithep::TEventInfo();
  mithep::TGenInfo   *gen   = new mithep::TGenInfo();
  TClonesArray *electronArr = new TClonesArray("mithep::TElectron");
  TClonesArray *pvArr       = new TClonesArray("mithep::TVertex");
  
  TFile *infile=0;
  TTree *eventTree=0;
  
  //
  // loop over samples
  //  
  for(UInt_t isam=0; isam<samplev.size(); isam++) {
    
    // Assume data sample is first sample in .conf file
    // If sample is empty (i.e. contains no ntuple files), skip to next sample
    if(isam==0 && !hasData) continue;
  
    CSample* samp = samplev[isam];
  
    //
    // Set up output ntuple
    //
    TString outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.root");
    if(isam==0 && !doScaleCorr) outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.raw.root");
    TFile *outFile = new TFile(outfilename,"RECREATE"); 
    TTree *outTree = new TTree("Events","Events");

    outTree->Branch("runNum",   &runNum,   "runNum/i");     // event run number
    outTree->Branch("lumiSec",  &lumiSec,  "lumiSec/i");    // event lumi section
    outTree->Branch("evtNum",   &evtNum,   "evtNum/i");     // event number
    outTree->Branch("npv",      &npv,      "npv/i");        // number of primary vertices
    outTree->Branch("npu",      &npu,      "npu/i");        // number of in-time PU events (MC)
    outTree->Branch("genVPt",   &genVPt,   "genVPt/F");     // GEN boson pT (signal MC)
    outTree->Branch("genVPhi",  &genVPhi,  "genVPhi/F");    // GEN boson phi (signal MC)
    outTree->Branch("genVy",    &genVy,    "genVy/F");      // GEN boson rapidity (signal MC)
    outTree->Branch("genVMass", &genVMass, "genVMass/F");   // GEN boson mass (signal MC)
    outTree->Branch("genLepPt", &genLepPt, "genLepPt/F");   // GEN lepton pT (signal MC)
    outTree->Branch("genLepPhi",&genLepPhi,"genLepPhi/F");  // GEN lepton phi (signal MC)
    outTree->Branch("scale1fb", &scale1fb, "scale1fb/F");   // event weight per 1/fb (MC)
    outTree->Branch("met",      &met,      "met/F");        // MET
    outTree->Branch("metPhi",   &metPhi,   "metPhi/F");     // phi(MET)
    outTree->Branch("sumEt",    &sumEt,    "sumEt/F");      // Sum ET
    outTree->Branch("mt",       &mt,       "mt/F");         // transverse mass
    outTree->Branch("u1",       &u1,       "u1/F");         // parallel component of recoil
    outTree->Branch("u2",       &u2,       "u2/F");         // perpendicular component of recoil
    outTree->Branch("q",        &q,        "q/I");          // lepton charge
    outTree->Branch("lep", "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &lep);  // lepton 4-vector
    ///// electron specific /////
    outTree->Branch("trkIso",    &trkIso,    "trkIso/F");     // track isolation of tag lepton
    outTree->Branch("emIso",     &emIso,     "emIso/F");      // ECAL isolation of tag lepton
    outTree->Branch("hadIso",    &hadIso,    "hadIso/F");     // HCAL isolation of tag lepton
    outTree->Branch("pfChIso",   &pfChIso,   "pfChIso/F");    // PF charged hadron isolation of lepton
    outTree->Branch("pfGamIso",  &pfGamIso,  "pfGamIso/F");   // PF photon isolation of lepton
    outTree->Branch("pfNeuIso",  &pfNeuIso,  "pfNeuIso/F");   // PF neutral hadron isolation of lepton
    outTree->Branch("pfCombIso", &pfCombIso, "pfCombIso/F");  // PF combined isolation of electron
    outTree->Branch("sigieie",   &sigieie,   "sigieie/F");    // sigma-ieta-ieta of electron
    outTree->Branch("hovere",    &hovere,    "hovere/F");     // H/E of electron
    outTree->Branch("eoverp",    &eoverp,    "eoverp/F");     // E/p of electron
    outTree->Branch("fbrem",     &fbrem,     "fbrem/F");      // brem fraction of electron
    outTree->Branch("dphi",      &dphi,	     "dphi/F");       // GSF track - ECAL dphi of electron
    outTree->Branch("deta",      &deta,      "deta/F");       // GSF track - ECAL deta of electron
    outTree->Branch("ecalE",     &ecalE,     "ecalE/F");      // ECAL energy of electron
    outTree->Branch("d0",        &d0,        "d0/F");         // transverse impact parameter of electron
    outTree->Branch("dz",        &dz,        "dz/F");         // longitudinal impact parameter of electron
    outTree->Branch("isConv",    &isConv,    "isConv/i");     // conversion filter flag of electron
    outTree->Branch("nexphits",  &nexphits,  "nexphits/i");   // number of missing expected inner hits of electron
    outTree->Branch("typeBits",  &typeBits,  "typeBits/i");   // electron type of electron
    outTree->Branch("sc",  "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &sc);   // electron Supercluster 4-vector
    
    //
    // loop through files
    //
    const UInt_t nfiles = samp->fnamev.size();
    for(UInt_t ifile=0; ifile<nfiles; ifile++) {  

      // Read input file and get the TTrees
      cout << "Processing " << samp->fnamev[ifile] << " [xsec = " << samp->xsecv[ifile] << " pb] ... "; cout.flush();      
      infile = new TFile(samp->fnamev[ifile]); 
      assert(infile);

      Bool_t hasJSON = kFALSE;
      mithep::RunLumiRangeMap rlrm;
      if(samp->jsonv[ifile].CompareTo("NONE")!=0) { 
        hasJSON = kTRUE;
        rlrm.AddJSONFile(samp->jsonv[ifile].Data()); 
      }
  
      eventTree = (TTree*)infile->Get("Events");
      assert(eventTree);  
      eventTree->SetBranchAddress("Info",     &info);        TBranch *infoBr     = eventTree->GetBranch("Info");
      eventTree->SetBranchAddress("Electron", &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");
      eventTree->SetBranchAddress("PV",       &pvArr);       TBranch *pvBr       = eventTree->GetBranch("PV");
      Bool_t hasGen = eventTree->GetBranchStatus("Gen");
      TBranch *genBr=0;
      if(hasGen) {
        eventTree->SetBranchAddress("Gen", &gen);
	genBr = eventTree->GetBranch("Gen");
      }
    
      // Compute MC event weight per 1/fb
      Double_t weight = 1;
      const Double_t xsec = samp->xsecv[ifile];
      if(xsec>0) weight = 1000.*xsec/(Double_t)eventTree->GetEntries();     

      //
      // loop over events
      //
      Double_t nsel=0, nselvar=0;
      for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
        infoBr->GetEntry(ientry);
	
	if(genBr) genBr->GetEntry(ientry);
     
        // check for certified lumi (if applicable)
        mithep::RunLumiRangeMap::RunLumiPairType rl(info->runNum, info->lumiSec);      
        if(hasJSON && !rlrm.HasRunLumi(rl)) continue;  

        // trigger requirement               
        ULong64_t trigger = kHLT_Ele22_CaloIdL_CaloIsoVL;
	ULong64_t trigObj = kHLT_Ele22_CaloIdL_CaloIsoVL_EleObj;   
        if(!(info->triggerBits & trigger)) continue;      
      
        // good vertex requirement
        if(!(info->hasGoodPV)) continue;
        pvArr->Clear();
        pvBr->GetEntry(ientry);
      
        //
	// SELECTION PROCEDURE:
	//  (1) Look for 1 good electron matched to trigger
	//  (2) Reject event if another electron is present passing looser cuts
	//
	electronArr->Clear();
        electronBr->GetEntry(ientry);
	Int_t nLooseLep=0;
	const mithep::TElectron *goodEle=0;
	Bool_t passSel=kFALSE;	
        for(Int_t i=0; i<electronArr->GetEntriesFast(); i++) {
          const mithep::TElectron *ele = (mithep::TElectron*)((*electronArr)[i]);
	  
	  // check ECAL gap
	  if(fabs(ele->scEta)>=ECAL_GAP_LOW && fabs(ele->scEta)<=ECAL_GAP_HIGH) continue;
	  
	  Double_t escale=1;
	  if(doScaleCorr && isam==0) {
	    for(UInt_t ieta=0; ieta<escaleNbins; ieta++) {
	      if(fabs(ele->scEta)<escaleEta[ieta]) {
	        escale = escaleCorr[ieta];
		break;
	      }
	    }
	  }
	  
	  if(fabs(ele->scEta)   > 2.5) continue;                // loose lepton |eta| cut
          if(escale*(ele->scEt) < 20)  continue;                // loose lepton pT cut
          if(passEleLooseID(ele,info->rhoLowEta)) nLooseLep++;  // loose lepton selection
          if(nLooseLep>1) {  // extra lepton veto
            passSel=kFALSE;
            break;
          }
          
          if(fabs(ele->scEta)   > ETA_CUT)    continue;  // lepton |eta| cut
          if(escale*(ele->scEt) < PT_CUT)     continue;  // lepton pT cut
          if(!passEleID(ele,info->rhoLowEta)) continue;  // lepton selection
          if(!(ele->hltMatchBits & trigObj))  continue;  // check trigger matching
	  
	  passSel=kTRUE;
	  goodEle = ele;  
	}
	
	if(passSel) {
	  
	  /******** We have a W candidate! HURRAY! ********/
	    
	  nsel+=weight;
          nselvar+=weight*weight;
	  
	  Double_t escale=1;
	  if(doScaleCorr && isam==0) {
	    for(UInt_t ieta=0; ieta<escaleNbins; ieta++) {
	      if(fabs(goodEle->scEta)<escaleEta[ieta]) {
	        escale = escaleCorr[ieta];
		break;
	      }
	    }
	  }
	  
	  LorentzVector vLep(escale*(goodEle->pt), goodEle->eta, goodEle->phi, ELE_MASS);  
	  LorentzVector vSC(escale*(goodEle->scEt), goodEle->scEta, goodEle->scPhi, ELE_MASS); 	  
	  
	  //
	  // Fill tree
	  //
	  runNum   = info->runNum;
	  lumiSec  = info->lumiSec;
	  evtNum   = info->evtNum;
	  npv	   = pvArr->GetEntriesFast();
	  npu	   = info->nPU;
	  genVPt   = 0;
	  genVPhi  = 0;
	  genVy    = 0;
	  genVMass = 0;
	  genLepPt = 0;
	  genLepPhi= 0;
	  u1       = 0;
	  u2       = 0;
	  if(hasGen) {
	    genVPt   = gen->vpt;
            genVPhi  = gen->vphi;
	    genVy    = gen->vy;
	    genVMass = gen->vmass;
	    TVector2 vWPt((gen->vpt)*cos(gen->vphi),(gen->vpt)*sin(gen->vphi));
	    TVector2 vLepPt(vLep.Px(),vLep.Py());      
            TVector2 vMet((info->pfMET)*cos(info->pfMETphi), (info->pfMET)*sin(info->pfMETphi));        
            TVector2 vU = -1.0*(vMet+vLepPt);
            u1 = ((vWPt.Px())*(vU.Px()) + (vWPt.Py())*(vU.Py()))/(gen->vpt);  // u1 = (pT . u)/|pT|
            u2 = ((vWPt.Px())*(vU.Py()) - (vWPt.Py())*(vU.Px()))/(gen->vpt);  // u2 = (pT x u)/|pT|
	    
	    if(abs(gen->id_1)==EGenType::kElectron) { genLepPt = gen->vpt_1; genLepPhi = gen->vphi_1; }
	    if(abs(gen->id_2)==EGenType::kElectron) { genLepPt = gen->vpt_2; genLepPhi = gen->vphi_2; }
	  }
	  scale1fb = weight;
	  met	   = info->pfMET;
	  metPhi   = info->pfMETphi;
	  sumEt    = info->pfSumET;
	  mt       = sqrt( 2.0 * (vLep.Pt()) * (info->pfMET) * (1.0-cos(toolbox::deltaPhi(vLep.Phi(),info->pfMETphi))) );
	  q        = goodEle->q;	  
	  lep      = &vLep;	  
	  
	  ///// electron specific /////
	  sc	    = &vSC;
	  trkIso    = goodEle->trkIso03;
	  emIso     = goodEle->emIso03;
	  hadIso    = goodEle->hadIso03;
	  pfChIso   = goodEle->pfChIso03;
	  pfGamIso  = goodEle->pfGamIso03;
	  pfNeuIso  = goodEle->pfNeuIso03;	
	  pfCombIso = goodEle->pfChIso03 + TMath::Max(goodEle->pfNeuIso03 + goodEle->pfGamIso03 - (info->rhoLowEta)*getEffArea(goodEle->scEta), 0.);
	  sigieie   = goodEle->sigiEtaiEta;
	  hovere    = goodEle->HoverE;
	  eoverp    = goodEle->EoverP;
	  fbrem     = goodEle->fBrem;
	  dphi      = goodEle->deltaPhiIn;
	  deta      = goodEle->deltaEtaIn;
	  d0        = goodEle->d0;
	  dz        = goodEle->dz;
	  isConv    = goodEle->isConv;
	  nexphits  = goodEle->nExpHitsInner;
	  typeBits  = goodEle->typeBits;
	   
	  outTree->Fill();
        }
      }
      delete infile;
      infile=0, eventTree=0;    

      cout << nsel  << " +/- " << sqrt(nselvar);
      if(isam!=0) cout << " per 1/fb";
      cout << endl;
    }
    outFile->Write();
    outFile->Close();
  }
  delete info;
  delete gen;
  delete electronArr;
  delete pvArr;
  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " W -> e nu" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  if(doScaleCorr)
    cout << "  *** Scale corrections applied ***" << endl;
  cout << endl;

  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("selectWe"); 
}
示例#2
0
void selectWe(const TString conf="we.conf", // input file
              const TString outputDir=".",  // output directory
              const Bool_t  doScaleCorr=0   // apply energy scale corrections?
             ) {
    gBenchmark->Start("selectWe");

    //--------------------------------------------------------------------------------------------------------------
    // Settings
    //==============================================================================================================

    const Double_t PT_CUT   = 25;
    const Double_t ETA_CUT  = 2.5;
    const Double_t ELE_MASS = 0.000511;

    const Double_t VETO_PT   = 10;
    const Double_t VETO_ETA  = 2.5;

    const Double_t ECAL_GAP_LOW  = 1.4442;
    const Double_t ECAL_GAP_HIGH = 1.566;

    const Double_t escaleNbins  = 2;
    const Double_t escaleEta[]  = { 1.4442, 2.5   };
    const Double_t escaleCorr[] = { 0.992,  1.009 };

    const Int_t BOSON_ID  = 24;
    const Int_t LEPTON_ID = 11;

    // load trigger menu
    const baconhep::TTrigger triggerMenu("../../BaconAna/DataFormats/data/HLT_50nsGRun");

    // load pileup reweighting file
    TFile *f_rw = TFile::Open("../Tools/pileup_rw_Golden.root", "read");
    TH1D *h_rw = (TH1D*) f_rw->Get("h_rw_golden");
    TH1D *h_rw_up = (TH1D*) f_rw->Get("h_rw_up_golden");
    TH1D *h_rw_down = (TH1D*) f_rw->Get("h_rw_down_golden");

    //--------------------------------------------------------------------------------------------------------------
    // Main analysis code
    //==============================================================================================================

    vector<TString>  snamev;      // sample name (for output files)
    vector<CSample*> samplev;     // data/MC samples

    //
    // parse .conf file
    //
    confParse(conf, snamev, samplev);
    const Bool_t hasData = (samplev[0]->fnamev.size()>0);

    // Create output directory
    gSystem->mkdir(outputDir,kTRUE);
    const TString ntupDir = outputDir + TString("/ntuples");
    gSystem->mkdir(ntupDir,kTRUE);

    //
    // Declare output ntuple variables
    //
    UInt_t  runNum, lumiSec, evtNum;
    UInt_t  npv, npu;
    UInt_t  id_1, id_2;
    Double_t x_1, x_2, xPDF_1, xPDF_2;
    Double_t scalePDF, weightPDF;
    TLorentzVector *genV=0, *genLep=0;
    Float_t genVPt, genVPhi, genVy, genVMass;
    Float_t genLepPt, genLepPhi;
    Float_t scale1fb, puWeight, puWeightUp, puWeightDown;
    Float_t met, metPhi, sumEt, mt, u1, u2;
    Float_t tkMet, tkMetPhi, tkSumEt, tkMt, tkU1, tkU2;
    Float_t mvaMet, mvaMetPhi, mvaSumEt, mvaMt, mvaU1, mvaU2;
    Float_t puppiMet, puppiMetPhi, puppiSumEt, puppiMt, puppiU1, puppiU2;
    Int_t   q;
    TLorentzVector *lep=0;
    Int_t lepID;
    ///// electron specific /////
    Float_t trkIso, emIso, hadIso;
    Float_t pfChIso, pfGamIso, pfNeuIso, pfCombIso;
    Float_t sigieie, hovere, eoverp, fbrem, ecalE;
    Float_t dphi, deta;
    Float_t d0, dz;
    UInt_t  isConv, nexphits, typeBits;
    TLorentzVector *sc=0;

    // Data structures to store info from TTrees
    baconhep::TEventInfo *info   = new baconhep::TEventInfo();
    baconhep::TGenEventInfo *gen = new baconhep::TGenEventInfo();
    TClonesArray *genPartArr     = new TClonesArray("baconhep::TGenParticle");
    TClonesArray *electronArr    = new TClonesArray("baconhep::TElectron");
    TClonesArray *scArr          = new TClonesArray("baconhep::TPhoton");
    TClonesArray *vertexArr      = new TClonesArray("baconhep::TVertex");

    TFile *infile=0;
    TTree *eventTree=0;

    //
    // loop over samples
    //
    for(UInt_t isam=0; isam<samplev.size(); isam++) {

        // Assume data sample is first sample in .conf file
        // If sample is empty (i.e. contains no ntuple files), skip to next sample
        Bool_t isData=kFALSE;
        if(isam==0 && !hasData) continue;
        else if (isam==0) isData=kTRUE;

        // Assume signal sample is given name "we" -- flag to store GEN W kinematics
        Bool_t isSignal = (snamev[isam].CompareTo("we",TString::kIgnoreCase)==0);
        // flag to reject W->enu events when selecting at wrong-flavor background events
        Bool_t isWrongFlavor = (snamev[isam].CompareTo("wx",TString::kIgnoreCase)==0);

        CSample* samp = samplev[isam];

        //
        // Set up output ntuple
        //
        TString outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.root");
        if(isam!=0 && !doScaleCorr) outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.raw.root");
        TFile *outFile = new TFile(outfilename,"RECREATE");
        TTree *outTree = new TTree("Events","Events");

        outTree->Branch("runNum",     &runNum,     "runNum/i");      // event run number
        outTree->Branch("lumiSec",    &lumiSec,    "lumiSec/i");     // event lumi section
        outTree->Branch("evtNum",     &evtNum,     "evtNum/i");      // event number
        outTree->Branch("npv",        &npv,        "npv/i");         // number of primary vertices
        outTree->Branch("npu",        &npu,        "npu/i");         // number of in-time PU events (MC)
        outTree->Branch("id_1",       &id_1,       "id_1/i");        // PDF info -- parton ID for parton 1
        outTree->Branch("id_2",       &id_2,       "id_2/i");        // PDF info -- parton ID for parton 2
        outTree->Branch("x_1",        &x_1,        "x_1/d");         // PDF info -- x for parton 1
        outTree->Branch("x_2",        &x_2,        "x_2/d");         // PDF info -- x for parton 2
        outTree->Branch("xPDF_1",     &xPDF_1,     "xPDF_1/d");      // PDF info -- x*F for parton 1
        outTree->Branch("xPDF_2",     &xPDF_2,     "xPDF_2/d");      // PDF info -- x*F for parton 2
        outTree->Branch("scalePDF",   &scalePDF,   "scalePDF/d");    // PDF info -- energy scale of parton interaction
        outTree->Branch("weightPDF",  &weightPDF,  "weightPDF/d");   // PDF info -- PDF weight
        outTree->Branch("genV",      "TLorentzVector", &genV);       // GEN boson 4-vector (signal MC)
        outTree->Branch("genLep",    "TLorentzVector", &genLep);     // GEN lepton 4-vector (signal MC)
        outTree->Branch("genVPt",     &genVPt,     "genVPt/F");      // GEN boson pT (signal MC)
        outTree->Branch("genVPhi",    &genVPhi,    "genVPhi/F");     // GEN boson phi (signal MC)
        outTree->Branch("genVy",      &genVy,      "genVy/F");       // GEN boson rapidity (signal MC)
        outTree->Branch("genVMass",   &genVMass,   "genVMass/F");    // GEN boson mass (signal MC)
        outTree->Branch("genLepPt",   &genLepPt,   "genLepPt/F");    // GEN lepton pT (signal MC)
        outTree->Branch("genLepPhi",  &genLepPhi,  "genLepPhi/F");   // GEN lepton phi (signal MC)
        outTree->Branch("scale1fb",   &scale1fb,   "scale1fb/F");    // event weight per 1/fb (MC)
        outTree->Branch("puWeight",   &puWeight,   "puWeight/F");    // scale factor for pileup reweighting (MC)
        outTree->Branch("puWeightUp",    &puWeightUp,   "puWeightUp/F");    // scale factor for pileup reweighting (MC)
        outTree->Branch("puWeightDown",    &puWeightDown,   "puWeightDown/F");    // scale factor for pileup reweighting (MC)
        outTree->Branch("met",        &met,        "met/F");         // MET
        outTree->Branch("metPhi",     &metPhi,     "metPhi/F");      // phi(MET)
        outTree->Branch("sumEt",      &sumEt,      "sumEt/F");       // Sum ET
        outTree->Branch("mt",         &mt,         "mt/F");          // transverse mass
        outTree->Branch("u1",         &u1,         "u1/F");          // parallel component of recoil
        outTree->Branch("u2",         &u2,         "u2/F");          // perpendicular component of recoil
        outTree->Branch("tkMet",      &tkMet,      "tkMet/F");       // MET (track MET)
        outTree->Branch("tkMetPhi",   &tkMetPhi,   "tkMetPhi/F");    // phi(MET) (track MET)
        outTree->Branch("tkSumEt",    &tkSumEt,    "tkSumEt/F");     // Sum ET (track MET)
        outTree->Branch("tkMt",       &tkMt,       "tkMt/F");        // transverse mass (track MET)
        outTree->Branch("tkU1",       &tkU1,       "tkU1/F");        // parallel component of recoil (track MET)
        outTree->Branch("tkU2",       &tkU2,       "tkU2/F");        // perpendicular component of recoil (track MET)
        outTree->Branch("mvaMet",     &mvaMet,     "mvaMet/F");      // MVA MET
        outTree->Branch("mvaMetPhi",  &mvaMetPhi,  "mvaMetPhi/F");   // phi(MVA MET)
        outTree->Branch("mvaSumEt",   &mvaSumEt,   "mvaSumEt/F");    // Sum ET (mva MET)
        outTree->Branch("mvaMt",      &mvaMt,      "mvaMt/F");       // transverse mass (mva MET)
        outTree->Branch("mvaU1",      &mvaU1,      "mvaU1/F");       // parallel component of recoil (mva MET)
        outTree->Branch("mvaU2",      &mvaU2,      "mvaU2/F");       // perpendicular component of recoil (mva MET)
        outTree->Branch("puppiMet",    &puppiMet,   "puppiMet/F");      // Puppi MET
        outTree->Branch("puppiMetPhi", &puppiMetPhi,"puppiMetPhi/F");   // phi(Puppi MET)
        outTree->Branch("puppiSumEt",  &puppiSumEt, "puppiSumEt/F");    // Sum ET (Puppi MET)
        outTree->Branch("puppiU1",     &puppiU1,    "puppiU1/F");       // parallel component of recoil (Puppi MET)
        outTree->Branch("puppiU2",     &puppiU2,    "puppiU2/F");       // perpendicular component of recoil (Puppi MET)
        outTree->Branch("q",          &q,          "q/I");           // lepton charge
        outTree->Branch("lep",       "TLorentzVector", &lep);        // lepton 4-vector
        outTree->Branch("lepID",      &lepID,      "lepID/I");       // lepton PDG ID
        ///// electron specific /////
        outTree->Branch("trkIso",     &trkIso,     "trkIso/F");      // track isolation of tag lepton
        outTree->Branch("emIso",      &emIso,      "emIso/F");       // ECAL isolation of tag lepton
        outTree->Branch("hadIso",     &hadIso,     "hadIso/F");      // HCAL isolation of tag lepton
        outTree->Branch("pfChIso",    &pfChIso,    "pfChIso/F");     // PF charged hadron isolation of lepton
        outTree->Branch("pfGamIso",   &pfGamIso,   "pfGamIso/F");    // PF photon isolation of lepton
        outTree->Branch("pfNeuIso",   &pfNeuIso,   "pfNeuIso/F");    // PF neutral hadron isolation of lepton
        outTree->Branch("pfCombIso",  &pfCombIso,  "pfCombIso/F");   // PF combined isolation of electron
        outTree->Branch("sigieie",    &sigieie,    "sigieie/F");     // sigma-ieta-ieta of electron
        outTree->Branch("hovere",     &hovere,     "hovere/F");      // H/E of electron
        outTree->Branch("eoverp",     &eoverp,     "eoverp/F");      // E/p of electron
        outTree->Branch("fbrem",      &fbrem,      "fbrem/F");       // brem fraction of electron
        outTree->Branch("dphi",       &dphi,       "dphi/F");        // GSF track - ECAL dphi of electron
        outTree->Branch("deta",       &deta,       "deta/F");        // GSF track - ECAL deta of electron
        outTree->Branch("ecalE",      &ecalE,      "ecalE/F");       // ECAL energy of electron
        outTree->Branch("d0",         &d0,         "d0/F");          // transverse impact parameter of electron
        outTree->Branch("dz",         &dz,         "dz/F");          // longitudinal impact parameter of electron
        outTree->Branch("isConv",     &isConv,     "isConv/i");      // conversion filter flag of electron
        outTree->Branch("nexphits",   &nexphits,   "nexphits/i");    // number of missing expected inner hits of electron
        outTree->Branch("typeBits",   &typeBits,   "typeBits/i");    // electron type of electron
        outTree->Branch("sc",        "TLorentzVector", &sc);         // supercluster 4-vector

        //
        // loop through files
        //
        const UInt_t nfiles = samp->fnamev.size();
        for(UInt_t ifile=0; ifile<nfiles; ifile++) {

            // Read input file and get the TTrees
            cout << "Processing " << samp->fnamev[ifile] << " [xsec = " << samp->xsecv[ifile] << " pb] ... ";
            cout.flush();
            infile = TFile::Open(samp->fnamev[ifile]);
            assert(infile);

            Bool_t hasJSON = kFALSE;
            baconhep::RunLumiRangeMap rlrm;
            if(samp->jsonv[ifile].CompareTo("NONE")!=0) {
                hasJSON = kTRUE;
                rlrm.addJSONFile(samp->jsonv[ifile].Data());
            }

            eventTree = (TTree*)infile->Get("Events");
            assert(eventTree);
            eventTree->SetBranchAddress("Info",     &info);
            TBranch *infoBr     = eventTree->GetBranch("Info");
            eventTree->SetBranchAddress("Electron", &electronArr);
            TBranch *electronBr = eventTree->GetBranch("Electron");
            eventTree->SetBranchAddress("PV",   &vertexArr);
            TBranch *vertexBr = eventTree->GetBranch("PV");

            Bool_t hasGen = eventTree->GetBranchStatus("GenEvtInfo");
            TBranch *genBr=0, *genPartBr=0;
            if(hasGen) {
                eventTree->SetBranchAddress("GenEvtInfo", &gen);
                genBr = eventTree->GetBranch("GenEvtInfo");
                eventTree->SetBranchAddress("GenParticle",&genPartArr);
                genPartBr = eventTree->GetBranch("GenParticle");
            }

            // Compute MC event weight per 1/fb
            const Double_t xsec = samp->xsecv[ifile];
            Double_t totalWeight=0;

            if (hasGen) {
                TH1D *hall = new TH1D("hall", "", 1,0,1);
                eventTree->Draw("0.5>>hall", "GenEvtInfo->weight");
                totalWeight=hall->Integral();
                delete hall;
                hall=0;
            }

            //
            // loop over events
            //
            Double_t nsel=0, nselvar=0;
            for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
                infoBr->GetEntry(ientry);

                if(ientry%1000000==0) cout << "Processing event " << ientry << ". " << (double)ientry/(double)eventTree->GetEntries()*100 << " percent done with this file." << endl;

                Double_t weight=1;
                if(xsec>0 && totalWeight>0) weight = xsec/totalWeight;
                if(hasGen) {
                    genPartArr->Clear();
                    genBr->GetEntry(ientry);
                    genPartBr->GetEntry(ientry);
                    weight*=gen->weight;
                }

                // veto w -> xv decays for signal and w -> ev for bacground samples (needed for inclusive WToLNu sample)
                if (isWrongFlavor && hasGen && fabs(toolbox::flavor(genPartArr, BOSON_ID))==LEPTON_ID) continue;
                else if (isSignal && hasGen && fabs(toolbox::flavor(genPartArr, BOSON_ID))!=LEPTON_ID) continue;

                // check for certified lumi (if applicable)
                baconhep::RunLumiRangeMap::RunLumiPairType rl(info->runNum, info->lumiSec);
                if(hasJSON && !rlrm.hasRunLumi(rl)) continue;

                // trigger requirement
                if (!isEleTrigger(triggerMenu, info->triggerBits, isData)) continue;

                // good vertex requirement
                if(!(info->hasGoodPV)) continue;

                //
                // SELECTION PROCEDURE:
                //  (1) Look for 1 good electron matched to trigger
                //  (2) Reject event if another electron is present passing looser cuts
                //
                electronArr->Clear();
                electronBr->GetEntry(ientry);

                Int_t nLooseLep=0;
                const baconhep::TElectron *goodEle=0;
                Bool_t passSel=kFALSE;

                for(Int_t i=0; i<electronArr->GetEntriesFast(); i++) {
                    const baconhep::TElectron *ele = (baconhep::TElectron*)((*electronArr)[i]);

                    // check ECAL gap
                    if(fabs(ele->scEta)>=ECAL_GAP_LOW && fabs(ele->scEta)<=ECAL_GAP_HIGH) continue;

                    // apply scale and resolution corrections to MC
                    Double_t elescEt_corr = ele->scEt;
                    if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0)
                        elescEt_corr = gRandom->Gaus(ele->scEt*getEleScaleCorr(ele->scEta,0),getEleResCorr(ele->scEta,0));

                    if(fabs(ele->scEta)   > VETO_ETA) continue;        // loose lepton |eta| cut
                    if(elescEt_corr       < VETO_PT)  continue;        // loose lepton pT cut
                    if(passEleLooseID(ele,info->rhoIso)) nLooseLep++;  // loose lepton selection
                    if(nLooseLep>1) {  // extra lepton veto
                        passSel=kFALSE;
                        break;
                    }

                    if(fabs(ele->scEta)   > ETA_CUT)     continue;  // lepton |eta| cut
                    if(elescEt_corr       < PT_CUT)      continue;  // lepton pT cut
                    if(!passEleID(ele,info->rhoIso))     continue;  // lepton selection
                    if(!isEleTriggerObj(triggerMenu, ele->hltMatchBits, kFALSE, isData)) continue;

                    passSel=kTRUE;
                    goodEle = ele;
                }

                if(passSel) {

                    //******* We have a W candidate! HURRAY! ********
                    nsel+=weight;
                    nselvar+=weight*weight;

                    // apply scale and resolution corrections to MC
                    Double_t goodElept_corr = goodEle->pt;
                    if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0)
                        goodElept_corr = gRandom->Gaus(goodEle->pt*getEleScaleCorr(goodEle->scEta,0),getEleResCorr(goodEle->scEta,0));

                    TLorentzVector vLep(0,0,0,0);
                    TLorentzVector vSC(0,0,0,0);
                    // apply scale and resolution corrections to MC
                    if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0) {
                        vLep.SetPtEtaPhiM(goodElept_corr, goodEle->eta, goodEle->phi, ELE_MASS);
                        vSC.SetPtEtaPhiM(gRandom->Gaus(goodEle->scEt*getEleScaleCorr(goodEle->scEta,0),getEleResCorr(goodEle->scEta,0)), goodEle->scEta, goodEle->scPhi, ELE_MASS);
                    } else {
                        vLep.SetPtEtaPhiM(goodEle->pt,goodEle->eta,goodEle->phi,ELE_MASS);
                        vSC.SetPtEtaPhiM(goodEle->scEt,goodEle->scEta,goodEle->scPhi,ELE_MASS);
                    }

                    //
                    // Fill tree
                    //
                    runNum    = info->runNum;
                    lumiSec   = info->lumiSec;
                    evtNum    = info->evtNum;

                    vertexArr->Clear();
                    vertexBr->GetEntry(ientry);

                    npv      = vertexArr->GetEntries();
                    npu	    = info->nPUmean;
                    genV      = new TLorentzVector(0,0,0,0);
                    genLep    = new TLorentzVector(0,0,0,0);
                    genVPt    = -999;
                    genVPhi   = -999;
                    genVy     = -999;
                    genVMass  = -999;
                    genLepPt  = -999;
                    genLepPhi = -999;
                    u1        = -999;
                    u2        = -999;
                    tkU1      = -999;
                    tkU2      = -999;
                    mvaU1     = -999;
                    mvaU2     = -999;
                    puppiU1     = -999;
                    puppiU2     = -999;
                    id_1      = -999;
                    id_2      = -999;
                    x_1       = -999;
                    x_2       = -999;
                    xPDF_1    = -999;
                    xPDF_2    = -999;
                    scalePDF  = -999;
                    weightPDF = -999;

                    if(isSignal && hasGen) {
                        TLorentzVector *gvec=new TLorentzVector(0,0,0,0);
                        TLorentzVector *glep1=new TLorentzVector(0,0,0,0);
                        TLorentzVector *glep2=new TLorentzVector(0,0,0,0);
                        toolbox::fillGen(genPartArr, BOSON_ID, gvec, glep1, glep2,1);

                        if (gvec && glep1) {
                            genV      = new TLorentzVector(0,0,0,0);
                            genV->SetPtEtaPhiM(gvec->Pt(),gvec->Eta(),gvec->Phi(),gvec->M());
                            genLep    = new TLorentzVector(0,0,0,0);
                            genLep->SetPtEtaPhiM(glep1->Pt(),glep1->Eta(),glep1->Phi(),glep1->M());
                            genVPt    = gvec->Pt();
                            genVPhi   = gvec->Phi();
                            genVy     = gvec->Rapidity();
                            genVMass  = gvec->M();
                            genLepPt  = glep1->Pt();
                            genLepPhi = glep1->Phi();

                            TVector2 vWPt((genVPt)*cos(genVPhi),(genVPt)*sin(genVPhi));
                            TVector2 vLepPt(vLep.Px(),vLep.Py());

                            TVector2 vMet((info->pfMETC)*cos(info->pfMETCphi), (info->pfMETC)*sin(info->pfMETCphi));
                            TVector2 vU = -1.0*(vMet+vLepPt);
                            u1 = ((vWPt.Px())*(vU.Px()) + (vWPt.Py())*(vU.Py()))/(genVPt);  // u1 = (pT . u)/|pT|
                            u2 = ((vWPt.Px())*(vU.Py()) - (vWPt.Py())*(vU.Px()))/(genVPt);  // u2 = (pT x u)/|pT|

                            TVector2 vTkMet((info->trkMET)*cos(info->trkMETphi), (info->trkMET)*sin(info->trkMETphi));
                            TVector2 vTkU = -1.0*(vTkMet+vLepPt);
                            tkU1 = ((vWPt.Px())*(vTkU.Px()) + (vWPt.Py())*(vTkU.Py()))/(genVPt);  // u1 = (pT . u)/|pT|
                            tkU2 = ((vWPt.Px())*(vTkU.Py()) - (vWPt.Py())*(vTkU.Px()))/(genVPt);  // u2 = (pT x u)/|pT|

                            TVector2 vMvaMet((info->mvaMET)*cos(info->mvaMETphi), (info->mvaMET)*sin(info->mvaMETphi));
                            TVector2 vMvaU = -1.0*(vMvaMet+vLepPt);
                            mvaU1 = ((vWPt.Px())*(vMvaU.Px()) + (vWPt.Py())*(vMvaU.Py()))/(genVPt);  // u1 = (pT . u)/|pT|
                            mvaU2 = ((vWPt.Px())*(vMvaU.Py()) - (vWPt.Py())*(vMvaU.Px()))/(genVPt);  // u2 = (pT x u)/|pT|

                            TVector2 vPuppiMet((info->puppET)*cos(info->puppETphi), (info->puppET)*sin(info->puppETphi));
                            TVector2 vPuppiU = -1.0*(vPuppiMet+vLepPt);
                            puppiU1 = ((vWPt.Px())*(vPuppiU.Px()) + (vWPt.Py())*(vPuppiU.Py()))/(genVPt);  // u1 = (pT . u)/|pT|
                            puppiU2 = ((vWPt.Px())*(vPuppiU.Py()) - (vWPt.Py())*(vPuppiU.Px()))/(genVPt);  // u2 = (pT x u)/|pT|

                        }
                        id_1      = gen->id_1;
                        id_2      = gen->id_2;
                        x_1       = gen->x_1;
                        x_2       = gen->x_2;
                        xPDF_1    = gen->xPDF_1;
                        xPDF_2    = gen->xPDF_2;
                        scalePDF  = gen->scalePDF;
                        weightPDF = gen->weight;

                        delete gvec;
                        delete glep1;
                        delete glep2;
                        gvec=0;
                        glep1=0;
                        glep2=0;
                    }
                    scale1fb = weight;
                    puWeight = h_rw->GetBinContent(h_rw->FindBin(npu));
                    puWeightUp = h_rw_up->GetBinContent(h_rw_up->FindBin(npu));
                    puWeightDown = h_rw_down->GetBinContent(h_rw_down->FindBin(npu));
                    met	   = info->pfMETC;
                    metPhi   = info->pfMETCphi;
                    sumEt    = 0;
                    mt       = sqrt( 2.0 * (vLep.Pt()) * (info->pfMETC) * (1.0-cos(toolbox::deltaPhi(vLep.Phi(),info->pfMETCphi))) );
                    tkMet	   = info->trkMET;
                    tkMetPhi = info->trkMETphi;
                    tkSumEt  = 0;
                    tkMt     = sqrt( 2.0 * (vLep.Pt()) * (info->trkMET) * (1.0-cos(toolbox::deltaPhi(vLep.Phi(),info->trkMETphi))) );
                    mvaMet   = info->mvaMET;
                    mvaMetPhi = info->mvaMETphi;
                    mvaSumEt  = 0;
                    mvaMt     = sqrt( 2.0 * (vLep.Pt()) * (info->mvaMET) * (1.0-cos(toolbox::deltaPhi(vLep.Phi(),info->mvaMETphi))) );
// 	  TVector2 vLepPt(vLep.Px(),vLep.Py());
// 	  TVector2 vPuppi((info->puppET)*cos(info->puppETphi), (info->puppET)*sin(info->puppETphi));
// 	  TVector2 vpp; vpp=vPuppi-vLepPt;
                    puppiMet   = info->puppET;
                    puppiMetPhi = info->puppETphi;
                    puppiSumEt  = 0;
                    puppiMt     = sqrt( 2.0 * (vLep.Pt()) * (info->puppET) * (1.0-cos(toolbox::deltaPhi(vLep.Phi(),info->puppETphi))) );
                    q        = goodEle->q;
                    lep      = &vLep;

                    ///// electron specific /////
                    sc       = &vSC;
                    trkIso    = goodEle->trkIso;
                    emIso     = goodEle->ecalIso;
                    hadIso    = goodEle->hcalIso;
                    pfChIso   = goodEle->chHadIso;
                    pfGamIso  = goodEle->gammaIso;
                    pfNeuIso  = goodEle->neuHadIso;
                    pfCombIso = goodEle->chHadIso + TMath::Max(goodEle->neuHadIso + goodEle->gammaIso -
                                (info->rhoIso)*getEffAreaEl(goodEle->scEta), 0.);
                    sigieie   = goodEle->sieie;
                    hovere    = goodEle->hovere;
                    eoverp    = goodEle->eoverp;
                    fbrem     = goodEle->fbrem;
                    dphi      = goodEle->dPhiIn;
                    deta      = goodEle->dEtaIn;
                    ecalE     = goodEle->ecalEnergy;
                    d0        = goodEle->d0;
                    dz        = goodEle->dz;
                    isConv    = goodEle->isConv;
                    nexphits  = goodEle->nMissingHits;
                    typeBits  = goodEle->typeBits;

                    outTree->Fill();
                    delete genV;
                    delete genLep;
                    genV=0, genLep=0, lep=0, sc=0;
                }
            }
            delete infile;
            infile=0, eventTree=0;

            cout << nsel  << " +/- " << sqrt(nselvar);
            if(isam!=0) cout << " per 1/pb";
            cout << endl;
        }
        outFile->Write();
        outFile->Close();
    }
    delete h_rw;
    delete h_rw_up;
    delete h_rw_down;
    delete f_rw;
    delete info;
    delete gen;
    delete genPartArr;
    delete electronArr;
    delete vertexArr;

    //--------------------------------------------------------------------------------------------------------------
    // Output
    //==============================================================================================================

    cout << "*" << endl;
    cout << "* SUMMARY" << endl;
    cout << "*--------------------------------------------------" << endl;
    cout << " W -> e nu" << endl;
    cout << "  pT > " << PT_CUT << endl;
    cout << "  |eta| < " << ETA_CUT << endl;
    if(doScaleCorr)
        cout << "  *** Scale corrections applied ***" << endl;
    cout << endl;

    cout << endl;
    cout << "  <> Output saved in " << outputDir << "/" << endl;
    cout << endl;

    gBenchmark->Show("selectWe");
}
void computeAccSelZeeBinned(const TString conf,            // input file
                            const TString outputDir,        // output directory
			    const Int_t   doPU
) {
  gBenchmark->Start("computeAccSelZeeBinned");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t MASS_LOW   = 60;
  const Double_t MASS_HIGH  = 120;
  const Double_t PT_CUT     = 25;
  const Double_t ETA_CUT    = 2.5;
  const Double_t ELE_MASS   = 0.000511;

  const Double_t ETA_BARREL = 1.4442;
  const Double_t ETA_ENDCAP = 1.566;

  const Int_t BOSON_ID  = 23;
  const Int_t LEPTON_ID = 11;
  
  // efficiency files
  const TString dataHLTEffName     = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/MG/eff.root";
  const TString dataHLTEffName_pos = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/MG/eff.root";
  const TString dataHLTEffName_neg = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/MG/eff.root";

  const TString zeeHLTEffName      = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/CT/eff.root";
  const TString zeeHLTEffName_pos  = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/CT/eff.root";
  const TString zeeHLTEffName_neg  = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/CT/eff.root";
  
  const TString dataGsfSelEffName     = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/MG/eff.root";
  const TString dataGsfSelEffName_pos = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/MG/eff.root";
  const TString dataGsfSelEffName_neg = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/MG/eff.root";

  const TString zeeGsfSelEffName      = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/CT/eff.root";
  const TString zeeGsfSelEffName_pos  = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/CT/eff.root";
  const TString zeeGsfSelEffName_neg  = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/CT/eff.root";

  // load pileup reweighting file
  TFile *f_rw = TFile::Open("../Tools/pileup_rw_76X.root", "read");
  TH1D *h_rw = (TH1D*) f_rw->Get("h_rw_golden");


  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  vector<TString> fnamev;  // file name per input file
  vector<TString> labelv;  // TLegend label per input file
  vector<Int_t>   colorv;  // plot color per input file
  vector<Int_t>   linev;   // plot line style per input file

  //
  // parse .conf file
  //
  ifstream ifs;
  ifs.open(conf.Data());
  assert(ifs.is_open());
  string line;
  while(getline(ifs,line)) {
    if(line[0]=='#') continue;
    
    string fname;
    Int_t color, linesty;
    stringstream ss(line);
    ss >> fname >> color >> linesty;
    string label = line.substr(line.find('@')+1);
    fnamev.push_back(fname);
    labelv.push_back(label);
    colorv.push_back(color);
    linev.push_back(linesty);
  }
  ifs.close();

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  
  TH2D *h=0;
  
  //
  // HLT efficiency
  //
  cout << "Loading trigger efficiencies..." << endl;

  TFile *dataHLTEffFile_pos = new TFile(dataHLTEffName_pos);
  CEffUser2D dataHLTEff_pos;
  dataHLTEff_pos.loadEff((TH2D*)dataHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataHLTEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataHLTEffFile_neg = new TFile(dataHLTEffName_neg);
  CEffUser2D dataHLTEff_neg;
  dataHLTEff_neg.loadEff((TH2D*)dataHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataHLTEffFile_neg->Get("hErrhEtaPt"));
  
  TFile *zeeHLTEffFile_pos = new TFile(zeeHLTEffName_pos);
  CEffUser2D zeeHLTEff_pos;
  zeeHLTEff_pos.loadEff((TH2D*)zeeHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)zeeHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zeeHLTEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *zeeHLTEffFile_neg = new TFile(zeeHLTEffName_neg);
  CEffUser2D zeeHLTEff_neg;
  zeeHLTEff_neg.loadEff((TH2D*)zeeHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)zeeHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zeeHLTEffFile_neg->Get("hErrhEtaPt"));
  
  h =(TH2D*)dataHLTEffFile_pos->Get("hEffEtaPt");
  TH2D *hHLTErr_pos = new TH2D("hHLTErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                 h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hHLTErr_neg = new TH2D("hHLTErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                 h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  
  //
  // Selection efficiency
  //
  cout << "Loading GSF+selection efficiencies..." << endl;
  
  TFile *dataGsfSelEffFile_pos = new TFile(dataGsfSelEffName_pos);
  CEffUser2D dataGsfSelEff_pos;
  dataGsfSelEff_pos.loadEff((TH2D*)dataGsfSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataGsfSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataGsfSelEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataGsfSelEffFile_neg = new TFile(dataGsfSelEffName_neg);
  CEffUser2D dataGsfSelEff_neg;
  dataGsfSelEff_neg.loadEff((TH2D*)dataGsfSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataGsfSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataGsfSelEffFile_neg->Get("hErrhEtaPt"));

  TFile *zeeGsfSelEffFile_pos = new TFile(zeeGsfSelEffName_pos);
  CEffUser2D zeeGsfSelEff_pos;
  zeeGsfSelEff_pos.loadEff((TH2D*)zeeGsfSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)zeeGsfSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zeeGsfSelEffFile_pos->Get("hErrhEtaPt"));

  TFile *zeeGsfSelEffFile_neg = new TFile(zeeGsfSelEffName_neg);
  CEffUser2D zeeGsfSelEff_neg;
  zeeGsfSelEff_neg.loadEff((TH2D*)zeeGsfSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)zeeGsfSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zeeGsfSelEffFile_neg->Get("hErrhEtaPt"));
 
  h =(TH2D*)dataGsfSelEffFile_pos->Get("hEffEtaPt");
  TH2D *hGsfSelErr_pos = new TH2D("hGsfSelErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hGsfSelErr_neg = new TH2D("hGsfSelErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  
  // Data structures to store info from TTrees
  baconhep::TEventInfo   *info = new baconhep::TEventInfo();
  baconhep::TGenEventInfo *gen = new baconhep::TGenEventInfo();
  TClonesArray *genPartArr     = new TClonesArray("baconhep::TGenParticle");
  TClonesArray *electronArr    = new TClonesArray("baconhep::TElectron");
  TClonesArray *vertexArr  = new TClonesArray("baconhep::TVertex");

  TFile *infile=0;
  TTree *eventTree=0;
  
  // Variables to store acceptances and uncertainties (per input file)
  vector<Double_t> nEvtsv, nSelv;
  vector<Double_t> nSelCorrv, nSelCorrVarv;
  vector<Double_t> accv, accCorrv;
  vector<Double_t> accErrv, accErrCorrv;

  const baconhep::TTrigger triggerMenu("../../BaconAna/DataFormats/data/HLT_50nsGRun");

  //
  // loop through files
  //
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {  

    // Read input file and get the TTrees
    cout << "Processing " << fnamev[ifile] << " ..." << endl;
    infile = TFile::Open(fnamev[ifile]); 
    assert(infile);

    eventTree = (TTree*)infile->Get("Events"); assert(eventTree);  
    eventTree->SetBranchAddress("Info",              &info); TBranch *infoBr     = eventTree->GetBranch("Info");
    eventTree->SetBranchAddress("GenEvtInfo",         &gen); TBranch *genBr      = eventTree->GetBranch("GenEvtInfo");
    eventTree->SetBranchAddress("GenParticle", &genPartArr); TBranch *genPartBr  = eventTree->GetBranch("GenParticle");
    eventTree->SetBranchAddress("Electron",   &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");
    eventTree->SetBranchAddress("PV",   &vertexArr); TBranch *vertexBr = eventTree->GetBranch("PV");

    nEvtsv.push_back(0);
    nSelv.push_back(0);
    nSelCorrv.push_back(0);
    nSelCorrVarv.push_back(0);

    //
    // loop over events
    //
    for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
      genBr->GetEntry(ientry);
      genPartArr->Clear(); genPartBr->GetEntry(ientry);
      infoBr->GetEntry(ientry);

      Int_t glepq1=-99;
      Int_t glepq2=-99;

      if (fabs(toolbox::flavor(genPartArr, BOSON_ID))!=LEPTON_ID) continue;
      TLorentzVector *vec=new TLorentzVector(0,0,0,0);
      TLorentzVector *lep1=new TLorentzVector(0,0,0,0);
      TLorentzVector *lep2=new TLorentzVector(0,0,0,0);
      toolbox::fillGen(genPartArr, BOSON_ID, vec, lep1, lep2,&glepq1,&glepq2,1);
      if(vec->M()<MASS_LOW || vec->M()>MASS_HIGH) continue;      
      delete vec; delete lep1; delete lep2;

      vertexArr->Clear();
      vertexBr->GetEntry(ientry);
      double npv  = vertexArr->GetEntries();
      Double_t weight=gen->weight;
      if(doPU>0) weight*=h_rw->GetBinContent(h_rw->FindBin(info->nPUmean));

      nEvtsv[ifile]+=weight;        

      // trigger requirement               
      if (!isEleTrigger(triggerMenu, info->triggerBits, kFALSE)) continue;
      
      // good vertex requirement
      if(!(info->hasGoodPV)) continue;
      
      electronArr->Clear();
      electronBr->GetEntry(ientry);

      for(Int_t i1=0; i1<electronArr->GetEntriesFast(); i1++) {
  	const baconhep::TElectron *ele1 = (baconhep::TElectron*)((*electronArr)[i1]);
	
	// check ECAL gap
	if(fabs(ele1->scEta)>=ETA_BARREL && fabs(ele1->scEta)<=ETA_ENDCAP) continue;
	
	//double ele1_pt = gRandom->Gaus(ele1->pt*getEleScaleCorr(ele1->scEta,0), getEleResCorr(ele1->scEta,0));

	if(ele1->pt	     < PT_CUT && ele1->scEt < PT_CUT)	  continue;  // lepton pT cut
        if(fabs(ele1->scEta) > ETA_CUT && fabs(ele1->eta) > ETA_CUT)	  continue;  // lepton |eta| cut
        if(!passEleID(ele1,info->rhoIso)) continue;  // lepton selection
	//if(!isEleTriggerObj(triggerMenu, ele1->hltMatchBits, kFALSE, kFALSE)) continue;

        TLorentzVector vEle1(0,0,0,0);
	vEle1.SetPtEtaPhiM(ele1->pt, ele1->eta, ele1->phi, ELE_MASS);
	Bool_t isB1 = (fabs(ele1->scEta)<ETA_BARREL) ? kTRUE : kFALSE;

        for(Int_t i2=i1+1; i2<electronArr->GetEntriesFast(); i2++) {          
	  const baconhep::TElectron *ele2 = (baconhep::TElectron*)((*electronArr)[i2]);
	  
	  // check ECAL gap
	  if(fabs(ele2->scEta)>=ETA_BARREL && fabs(ele2->scEta)<=ETA_ENDCAP) continue;
	  //double ele2_pt = gRandom->Gaus(ele2->scEt*getEleScaleCorr(ele2->scEta,0), getEleResCorr(ele2->scEta,0));
	  if(ele1->q == ele2->q)	continue;
          if(ele2->pt        < PT_CUT && ele2->scEt < PT_CUT)    continue;  // lepton pT cut
          if(fabs(ele2->scEta) > ETA_CUT && fabs(ele2->eta) > ETA_CUT)   continue;  // lepton |eta| cut
	  if(!passEleID(ele2,info->rhoIso)) continue;  // lepton selection

          TLorentzVector vEle2(0,0,0,0);
	  vEle2.SetPtEtaPhiM(ele2->pt, ele2->eta, ele2->phi, ELE_MASS);  
          Bool_t isB2 = (fabs(ele2->scEta)<ETA_BARREL) ? kTRUE : kFALSE;

	  if(!isEleTriggerObj(triggerMenu, ele1->hltMatchBits, kFALSE, kFALSE) && !isEleTriggerObj(triggerMenu, ele2->hltMatchBits, kFALSE, kFALSE)) continue;
	  
	  // mass window
          TLorentzVector vDilep = vEle1 + vEle2;
          if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;
          
          /******** We have a Z candidate! HURRAY! ********/
          Double_t effdata, effmc;
          //Double_t sceta1 = (fabs(ele1->scEta)<2.5) ? ele1->scEta : 0.99*(ele1->scEta);
	  Double_t sceta1 = ele1->scEta;
          //Double_t sceta2 = (fabs(ele2->scEta)<2.5) ? ele2->scEta : 0.99*(ele2->scEta);
    	  Double_t sceta2 = ele2->scEta;

          Double_t corr=1;
	  
	  effdata=1; effmc=1;
          if(ele1->q>0) { 
            effdata *= (1.-dataHLTEff_pos.getEff(sceta1, ele1->scEt));
            effmc   *= (1.-zeeHLTEff_pos.getEff(sceta1, ele1->scEt));
          } else {
            effdata *= (1.-dataHLTEff_neg.getEff(sceta1, ele1->scEt)); 
            effmc   *= (1.-zeeHLTEff_neg.getEff(sceta1, ele1->scEt)); 
          }
          if(ele2->q>0) {
            effdata *= (1.-dataHLTEff_pos.getEff(sceta2, ele2->scEt)); 
            effmc   *= (1.-zeeHLTEff_pos.getEff(sceta2, ele2->scEt));
          } else {
            effdata *= (1.-dataHLTEff_neg.getEff(sceta2, ele2->scEt)); 
            effmc   *= (1.-zeeHLTEff_neg.getEff(sceta2, ele2->scEt));
          }
          effdata = 1.-effdata;
          effmc   = 1.-effmc;
          corr *= effdata/effmc;


          effdata=1; effmc=1;
          if(ele1->q>0) { 
            effdata *= dataGsfSelEff_pos.getEff(sceta1, ele1->scEt); 
            effmc   *= zeeGsfSelEff_pos.getEff(sceta1, ele1->scEt); 
          } else {
            effdata *= dataGsfSelEff_neg.getEff(sceta1, ele1->scEt); 
            effmc   *= zeeGsfSelEff_neg.getEff(sceta1, ele1->scEt); 
          }
          if(ele2->q>0) {
            effdata *= dataGsfSelEff_pos.getEff(sceta2, ele2->scEt); 
            effmc   *= zeeGsfSelEff_pos.getEff(sceta2, ele2->scEt);
          } else {
            effdata *= dataGsfSelEff_neg.getEff(sceta2, ele2->scEt); 
            effmc   *= zeeGsfSelEff_neg.getEff(sceta2, ele2->scEt);
          }
          corr *= effdata/effmc;
	  
	  // scale factor uncertainties
	  if(ele1->q>0) {	    
	    Double_t effdata = dataGsfSelEff_pos.getEff(sceta1, ele1->scEt);
	    Double_t errdata = TMath::Max(dataGsfSelEff_pos.getErrLow(sceta1, ele1->scEt), dataGsfSelEff_pos.getErrHigh(sceta1, ele1->scEt));
            Double_t effmc   = zeeGsfSelEff_pos.getEff(sceta1, ele1->scEt); 
	    Double_t errmc   = TMath::Max(zeeGsfSelEff_pos.getErrLow(sceta1, ele1->scEt), zeeGsfSelEff_pos.getErrHigh(sceta1, ele1->scEt));
	    Double_t errGsfSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
	    hGsfSelErr_pos->Fill(sceta1, ele1->scEt, errGsfSel);
	  } else {
	    Double_t effdata = dataGsfSelEff_neg.getEff(sceta1, ele1->scEt);
	    Double_t errdata = TMath::Max(dataGsfSelEff_neg.getErrLow(sceta1, ele1->scEt), dataGsfSelEff_neg.getErrHigh(sceta1, ele1->scEt));
            Double_t effmc   = zeeGsfSelEff_neg.getEff(sceta1, ele1->scEt); 
	    Double_t errmc   = TMath::Max(zeeGsfSelEff_neg.getErrLow(sceta1, ele1->scEt), zeeGsfSelEff_neg.getErrHigh(sceta1, ele1->scEt));
	    Double_t errGsfSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
	    hGsfSelErr_neg->Fill(sceta1, ele1->scEt, errGsfSel);
	  }

	  if(ele2->q>0) {	    
	    Double_t effdata = dataHLTEff_pos.getEff(sceta2, ele2->scEt);
	    Double_t errdata = TMath::Max(dataHLTEff_pos.getErrLow(sceta2, ele2->scEt), dataHLTEff_pos.getErrHigh(sceta2, ele2->scEt));
            Double_t effmc   = zeeHLTEff_pos.getEff(sceta2, ele2->scEt); 
	    Double_t errmc   = TMath::Max(zeeHLTEff_pos.getErrLow(sceta2, ele2->scEt), zeeHLTEff_pos.getErrHigh(sceta2, ele2->scEt));
	    Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
	    hHLTErr_pos->Fill(sceta2, ele2->scEt, errHLT);
	  } else {
	    Double_t effdata = dataHLTEff_neg.getEff(sceta2, ele2->scEt);
	    Double_t errdata = TMath::Max(dataHLTEff_neg.getErrLow(sceta2, ele2->scEt), dataHLTEff_neg.getErrHigh(sceta2, ele2->scEt));
            Double_t effmc   = zeeHLTEff_neg.getEff(sceta2, ele2->scEt); 
	    Double_t errmc   = TMath::Max(zeeHLTEff_neg.getErrLow(sceta2, ele2->scEt), zeeHLTEff_neg.getErrHigh(sceta2, ele2->scEt));
	    Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
	    hHLTErr_neg->Fill(sceta2, ele2->scEt, errHLT);
	  }

	  nSelv[ifile]+=weight;
	  nSelCorrv[ifile]+=weight*corr;
	  nSelCorrVarv[ifile]+=weight*weight*corr*corr;
	}
      }
    }
    
    Double_t var=0;
    for(Int_t iy=0; iy<=hHLTErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hHLTErr_pos->GetNbinsX(); ix++) {
	Double_t err=hHLTErr_pos->GetBinContent(ix,iy);
	var+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hHLTErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hHLTErr_neg->GetNbinsX(); ix++) {
	Double_t err=hHLTErr_neg->GetBinContent(ix,iy);
	var+=err*err;
      }
    }
    cout << "var1: " << var << endl;
    for(Int_t iy=0; iy<=hGsfSelErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hGsfSelErr_pos->GetNbinsX(); ix++) {
	Double_t err=hGsfSelErr_pos->GetBinContent(ix,iy);
	var+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hGsfSelErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hGsfSelErr_neg->GetNbinsX(); ix++) {
	Double_t err=hGsfSelErr_neg->GetBinContent(ix,iy);
	var+=err*err;
      }
    }
    cout << "var2: " << var << endl;

    nSelCorrVarv[ifile]+=var;

    // compute acceptances
    std::cout << nEvtsv[ifile] << " " << nSelv[ifile] << std::endl;
    accv.push_back(nSelv[ifile]/nEvtsv[ifile]);         accErrv.push_back(sqrt(accv[ifile]*(1. +accv[ifile])/nEvtsv[ifile]));
    accCorrv.push_back(nSelCorrv[ifile]/nEvtsv[ifile]); accErrCorrv.push_back(accCorrv[ifile]*sqrt((nSelCorrVarv[ifile])/(nSelCorrv[ifile]*nSelCorrv[ifile]) + 1./nEvtsv[ifile]));
    
    delete infile;
    infile=0, eventTree=0;  
  }  
  delete info;
  delete gen;
  delete electronArr;  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> e e" << endl;
  cout << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  cout << endl;
  
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {
    cout << "   ================================================" << endl;
    cout << "    Label: " << labelv[ifile] << endl;
    cout << "     File: " << fnamev[ifile] << endl;
    cout << endl;
    cout << "    *** Acceptance ***" << endl;
    cout << "          nominal: " << setw(12) << nSelv[ifile]   << " / " << nEvtsv[ifile] << " = " << accv[ifile]   << " +/- " << accErrv[ifile] << endl;
    cout << "     SF corrected: " << accCorrv[ifile] << " +/- " << accErrCorrv[ifile] << endl;
    cout << endl;
  }
  
  char txtfname[100];
  sprintf(txtfname,"%s/binned.txt",outputDir.Data());
  ofstream txtfile;
  txtfile.open(txtfname);
  txtfile << "*" << endl;
  txtfile << "* SUMMARY" << endl;
  txtfile << "*--------------------------------------------------" << endl;
  txtfile << " Z -> e e" << endl;
  txtfile << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  txtfile << "  pT > " << PT_CUT << endl;
  txtfile << "  |eta| < " << ETA_CUT << endl;
  txtfile << endl;
  
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {
    txtfile << "   ================================================" << endl;
    txtfile << "    Label: " << labelv[ifile] << endl;
    txtfile << "     File: " << fnamev[ifile] << endl;
    txtfile << endl;
    txtfile << "    *** Acceptance ***" << endl;
    txtfile << "          nominal: " << setw(12) << nSelv[ifile]   << " / " << nEvtsv[ifile] << " = " << accv[ifile]   << " +/- " << accErrv[ifile] << endl;
    txtfile << "     SF corrected: " << accCorrv[ifile] << " +/- " << accErrCorrv[ifile] << endl;
    txtfile << endl;
  }
  txtfile.close();
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("computeAccSelZeeBinned"); 
}
void computeAccSelWe_Charge(const TString conf,       // input file
                     const TString outputDir,  // output directory
		     const Int_t   charge,      // 0 = inclusive, +1 = W+, -1 = W-
		     const Int_t   doPU,
		     const Int_t   doScaleCorr,
		     const Int_t   sigma
) {
  gBenchmark->Start("computeAccSelWe");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t PT_CUT     = 25;
  const Double_t ETA_CUT    = 2.5;
  const Double_t ELE_MASS = 0.000511;

  const Double_t ETA_BARREL = 1.4442;
  const Double_t ETA_ENDCAP = 1.566;

  const Double_t VETO_PT   = 10;
  const Double_t VETO_ETA  = 2.5;

  const Double_t ECAL_GAP_LOW  = 1.4442;
  const Double_t ECAL_GAP_HIGH = 1.566;

  const Int_t BOSON_ID  = 24;
  const Int_t LEPTON_ID = 11;
 
  // efficiency files
  TString dataHLTEffName(   "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/MG/eff.root");
  TString zeeHLTEffName(    "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/CT/eff.root");
  TString dataGsfSelEffName("/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/MG/eff.root");
  TString zeeGsfSelEffName( "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/CT/eff.root");
  if(charge==1) {
    dataHLTEffName    = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/MGpositive/eff.root";
    zeeHLTEffName     = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/CTpositive/eff.root"; 
    dataGsfSelEffName = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/MGpositive_FineBin/eff.root";
    zeeGsfSelEffName  = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/CTpositive/eff.root"; 
  }
  if(charge==-1) {
    dataHLTEffName    = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/MGnegative/eff.root";
    zeeHLTEffName     = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/CTnegative/eff.root";
    dataGsfSelEffName = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/MGnegative_FineBin/eff.root";
    zeeGsfSelEffName  = "/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleGsfSelEff/CTnegative/eff.root";
  }

  const TString corrFiles = "../EleScale/76X_16DecRereco_2015_Etunc";

  EnergyScaleCorrection_class eleCorr( corrFiles.Data()); eleCorr.doScale= true; eleCorr.doSmearings =true;

  // load pileup reweighting file
  TFile *f_rw = TFile::Open("../Tools/puWeights_76x.root", "read");
  TH1D *h_rw = (TH1D*) f_rw->Get("puWeights");

  TFile *f_r9 = TFile::Open("../EleScale/transformation.root","read");
  TGraph* gR9EB = (TGraph*) f_r9->Get("transformR90");
  TGraph* gR9EE = (TGraph*) f_r9->Get("transformR91");

  TFile *f_hlt_data;
  TFile *f_hlt_mc;

  if(charge==1){
    f_hlt_data = TFile::Open("/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/Nominal/EleTriggerTF1_Data_Positive.root");
    f_hlt_mc   = TFile::Open("/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/Nominal/EleTriggerTF1_MC_Positive.root");
  }
  if(charge==-1){
    f_hlt_data = TFile::Open("/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/Nominal/EleTriggerTF1_Data_Negative.root");
    f_hlt_mc   = TFile::Open("/afs/cern.ch/work/x/xniu/public/WZXSection/wz-efficiency/EleHLTEff/Nominal/EleTriggerTF1_MC_Negative.root");
  }
 
  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  vector<TString> fnamev;  // file name per input file
  vector<TString> labelv;  // TLegend label per input file
  vector<Int_t>   colorv;  // plot color per input file
  vector<Int_t>   linev;   // plot line style per input file

  //
  // parse .conf file
  //
  ifstream ifs;
  ifs.open(conf.Data());
  assert(ifs.is_open());
  string line;
  while(getline(ifs,line)) {
    if(line[0]=='#') continue;
    
    string fname;
    Int_t color, linesty;
    stringstream ss(line);
    ss >> fname >> color >> linesty;
    string label = line.substr(line.find('@')+1);
    fnamev.push_back(fname);
    labelv.push_back(label);
    colorv.push_back(color);
    linev.push_back(linesty);
  }
  ifs.close();

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  
  //
  // Get efficiency
  //
  TFile *dataHLTEffFile = new TFile(dataHLTEffName);
  CEffUser2D dataHLTEff;
  TH2D *hHLTErr=0, *hHLTErrB=0, *hHLTErrE=0;
  if(dataHLTEffName) {
    dataHLTEff.loadEff((TH2D*)dataHLTEffFile->Get("hEffEtaPt"),
                       (TH2D*)dataHLTEffFile->Get("hErrlEtaPt"),
                       (TH2D*)dataHLTEffFile->Get("hErrhEtaPt"));
    
    TH2D* h =(TH2D*)dataHLTEffFile->Get("hEffEtaPt");
    hHLTErr  = new TH2D("hHLTErr", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                      h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
    hHLTErrB = new TH2D("hHLTErrB","",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                      h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
    hHLTErrE = new TH2D("hHLTErrE","",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                      h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  }
  
  TFile *zeeHLTEffFile = new TFile(zeeHLTEffName);
  CEffUser2D zeeHLTEff;
  if(zeeHLTEffName) {
    zeeHLTEff.loadEff((TH2D*)zeeHLTEffFile->Get("hEffEtaPt"),
                      (TH2D*)zeeHLTEffFile->Get("hErrlEtaPt"),
                      (TH2D*)zeeHLTEffFile->Get("hErrhEtaPt"));
  }
  
  TFile *dataGsfSelEffFile = new TFile(dataGsfSelEffName);
  CEffUser2D dataGsfSelEff;
  TH2D *hGsfSelErr=0, *hGsfSelErrB=0, *hGsfSelErrE=0;
  if(dataGsfSelEffName) {
    dataGsfSelEff.loadEff((TH2D*)dataGsfSelEffFile->Get("hEffEtaPt"),
                       (TH2D*)dataGsfSelEffFile->Get("hErrlEtaPt"),
                       (TH2D*)dataGsfSelEffFile->Get("hErrhEtaPt"));
    
    TH2D* h =(TH2D*)dataGsfSelEffFile->Get("hEffEtaPt");
    hGsfSelErr  = new TH2D("hGsfSelErr", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                      h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
    hGsfSelErrB = new TH2D("hGsfSelErrB","",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                      h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
    hGsfSelErrE = new TH2D("hGsfSelErrE","",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                      h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  }
  
  TFile *zeeGsfSelEffFile = new TFile(zeeGsfSelEffName);
  CEffUser2D zeeGsfSelEff;
  if(zeeGsfSelEffName) {
    zeeGsfSelEff.loadEff((TH2D*)zeeGsfSelEffFile->Get("hEffEtaPt"),
                      (TH2D*)zeeGsfSelEffFile->Get("hErrlEtaPt"),
                      (TH2D*)zeeGsfSelEffFile->Get("hErrhEtaPt"));
  }
  
  // Data structures to store info from TTrees
  baconhep::TEventInfo    *info = new baconhep::TEventInfo();
  baconhep::TGenEventInfo *gen  = new baconhep::TGenEventInfo();
  TClonesArray *electronArr = new TClonesArray("baconhep::TElectron");
  TClonesArray *genPartArr  = new TClonesArray("baconhep::TGenParticle");
  TClonesArray *vertexArr  = new TClonesArray("baconhep::TVertex");
  
  TFile *infile=0;
  TTree *eventTree=0;

  // Variables to store acceptances and uncertainties (per input file)
  vector<Double_t> nEvtsv, nSelv, nSelBv, nSelEv;
  vector<Double_t> accv, accBv, accEv;
  vector<Double_t> accErrv, accErrBv, accErrEv;
  vector<Double_t> nSelCorrv, nSelBCorrv, nSelECorrv;
  vector<Double_t> nSelCorrVarv, nSelBCorrVarv, nSelECorrVarv;
  vector<Double_t> accCorrv, accBCorrv, accECorrv;
  vector<Double_t> accErrCorrv, accErrBCorrv, accErrECorrv;

  const baconhep::TTrigger triggerMenu("../../BaconAna/DataFormats/data/HLT_50nsGRun");
 
  // loop through files
  //
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {  

    // Read input file and get the TTrees
    cout << "Processing " << fnamev[ifile] << " ..." << endl;
    infile = TFile::Open(fnamev[ifile]); 
    assert(infile);
  
    eventTree = (TTree*)infile->Get("Events"); assert(eventTree);  
    eventTree->SetBranchAddress("Info",             &info); TBranch *infoBr     = eventTree->GetBranch("Info");
    eventTree->SetBranchAddress("GenEvtInfo",        &gen); TBranch *genBr      = eventTree->GetBranch("GenEvtInfo");
    eventTree->SetBranchAddress("GenParticle",&genPartArr); TBranch *genPartBr  = eventTree->GetBranch("GenParticle");
    eventTree->SetBranchAddress("Electron",  &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");
    eventTree->SetBranchAddress("PV",   &vertexArr); TBranch *vertexBr = eventTree->GetBranch("PV");

    nEvtsv.push_back(0);
    nSelv.push_back(0);
    nSelBv.push_back(0);
    nSelEv.push_back(0);
    nSelCorrv.push_back(0);
    nSelBCorrv.push_back(0);
    nSelECorrv.push_back(0);
    nSelCorrVarv.push_back(0);
    nSelBCorrVarv.push_back(0);
    nSelECorrVarv.push_back(0);
    
    for(Int_t iy=0; iy<=hHLTErr->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hHLTErr->GetNbinsX(); ix++) {
        hHLTErr ->SetBinContent(ix,iy,0);
        hHLTErrB->SetBinContent(ix,iy,0);
        hHLTErrE->SetBinContent(ix,iy,0);
      }
    }
    for(Int_t iy=0; iy<=hGsfSelErr->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hGsfSelErr->GetNbinsX(); ix++) {
        hGsfSelErr ->SetBinContent(ix,iy,0);
        hGsfSelErrB->SetBinContent(ix,iy,0);
        hGsfSelErrE->SetBinContent(ix,iy,0);
      }
    }

    //
    // loop over events
    //
    for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
//      if(ientry==10000) break;
      infoBr->GetEntry(ientry);
      genBr->GetEntry(ientry);      
      genPartArr->Clear(); genPartBr->GetEntry(ientry);
  
      if (charge==-1 && toolbox::flavor(genPartArr, -BOSON_ID)!=LEPTON_ID) continue;
      if (charge==1 && toolbox::flavor(genPartArr, BOSON_ID)!=-LEPTON_ID) continue;
      if (charge==0 && fabs(toolbox::flavor(genPartArr, BOSON_ID))!=LEPTON_ID) continue;
    
      vertexArr->Clear();
      vertexBr->GetEntry(ientry);
      double npv  = vertexArr->GetEntries();
      Double_t weight=gen->weight;
      if(doPU>0) weight*=h_rw->GetBinContent(h_rw->FindBin(info->nPUmean));

      nEvtsv[ifile]+=weight;
      
      // trigger requirement                
      if (!isEleTrigger(triggerMenu, info->triggerBits, kFALSE)) continue;
      
      // good vertex requirement
      if(!(info->hasGoodPV)) continue;
      
      electronArr->Clear();
      electronBr->GetEntry(ientry);
      Int_t nLooseLep=0;
      const baconhep::TElectron *goodEle=0;
      TLorentzVector vEle(0,0,0,0);
      TLorentzVector vElefinal(0,0,0,0);
      Bool_t passSel=kFALSE;
     
      for(Int_t i=0; i<electronArr->GetEntriesFast(); i++) {
        const baconhep::TElectron *ele = (baconhep::TElectron*)((*electronArr)[i]);
	vEle.SetPtEtaPhiM(ele->pt, ele->eta, ele->phi, ELE_MASS);
	
	//double ele_pt = gRandom->Gaus(ele->scEt*getEleScaleCorr(ele->scEta,0), getEleResCorr(ele->scEta,0));
        //double ele_pt = gRandom->Gaus(ele->scEt*getEleScaleCorr(ele->scEta,0), getEleResCorr(ele->scEta,0));

        // check ECAL gap
//        if(fabs(ele->scEta)>=ETA_BARREL && fabs(ele->scEta)<=ETA_ENDCAP) continue;
	if(fabs(vEle.Eta())>=ECAL_GAP_LOW && fabs(vEle.Eta())<=ECAL_GAP_HIGH) continue;
        if(doScaleCorr && (ele->r9 < 1.)){
            float eleSmear = 0.;

            float eleAbsEta   = fabs(vEle.Eta());
            float eleEt       = vEle.E() / cosh(eleAbsEta);
            bool  eleisBarrel = eleAbsEta < 1.4442;

            float eleR9Prime = ele->r9; // r9 corrections MC only
            if(eleisBarrel){
                      eleR9Prime = gR9EB->Eval(ele->r9);}
            else {
                      eleR9Prime = gR9EE->Eval(ele->r9);
            }

            double eleRamdom = gRandom->Gaus(0,1);
            eleSmear = eleCorr.getSmearingSigma(info->runNum, eleisBarrel, eleR9Prime, eleAbsEta, eleEt, 0., 0.);
            float eleSmearEP = eleCorr.getSmearingSigma(info->runNum, eleisBarrel, eleR9Prime, eleAbsEta, eleEt, 1., 0.);
            float eleSmearEM = eleCorr.getSmearingSigma(info->runNum, eleisBarrel, eleR9Prime, eleAbsEta, eleEt, -1., 0.);

            if(sigma==0){
              (vEle) *= 1. + eleSmear * eleRamdom;
            }else if(sigma==1){
              (vEle) *= 1. + eleSmearEP * eleRamdom;
            }else if(sigma==-1){
              (vEle) *= 1.  + eleSmearEM * eleRamdom;
            }
        }

        
//        if(fabs(ele->scEta) > VETO_ETA) continue;             // loose lepton |eta| cut
//        if(ele->scEt < VETO_PT)  continue;             // loose lepton pT cut
//        if(passEleLooseID(ele,info->rhoIso)) nLooseLep++;     // loose lepton selection
        if(fabs(vEle.Eta())    > VETO_ETA) continue;
        if(vEle.Pt()           < VETO_PT)  continue;
        if(passEleLooseID(ele, vEle, info->rhoIso)) nLooseLep++;

        if(nLooseLep>1) {  // extra lepton veto
          passSel=kFALSE;
          break;
        }

//        if(fabs(ele->scEta) > ETA_CUT && fabs(ele->eta) > ETA_CUT)       continue;  // lepton |eta| cut
//        if(ele->pt < PT_CUT && ele->scEt < PT_CUT)  	     continue;  // lepton pT cut
//        if(!passEleID(ele,info->rhoIso))     continue;  // lepton selection
        if(vEle.Pt()           < PT_CUT)     continue;  // lepton pT cut
        if(fabs(vEle.Eta())    > ETA_CUT)    continue;  // lepton |eta| cut
        if(!passEleID(ele, vEle, info->rhoIso))     continue;  // lepton selection

	if(!isEleTriggerObj(triggerMenu, ele->hltMatchBits, kFALSE, kFALSE)) continue;
        //if(!(ele->hltMatchBits[trigObjHLT])) continue;  // check trigger matching

	if(charge!=0 && ele->q!=charge) continue;  // check charge (if necessary)
        
	passSel=kTRUE;
        goodEle = ele;  
	vElefinal = vEle;
      }
      
      if(passSel) {
        
	/******** We have a W candidate! HURRAY! ********/
        Bool_t isBarrel = (fabs(vElefinal.Eta())<ETA_BARREL) ? kTRUE : kFALSE;

        Double_t corr=1;
        if(dataHLTEffFile && zeeHLTEffFile) {
//          Double_t effdata = dataHLTEff.getEff(vElefinal.Eta(), vElefinal.Pt());
//          Double_t effmc   = zeeHLTEff.getEff(vElefinal.Eta(), vElefinal.Pt());

	  char funcname[20];
	  sprintf(funcname, "fitfcn_%d", getEtaBinLabel(vElefinal.Eta()));
	  TF1 *fdt = (TF1*)f_hlt_data->Get(funcname);
	  TF1 *fmc = (TF1*)f_hlt_mc  ->Get(funcname);
	  Double_t effdata = fdt->Eval(TMath::Min(vElefinal.Pt(),119.0));
	  Double_t effmc   = fmc->Eval(TMath::Min(vElefinal.Pt(),119.0));
	  delete fdt;
	  delete fmc;

	  corr *= effdata/effmc;
        }
        if(dataGsfSelEffFile && zeeGsfSelEffFile) {
          Double_t effdata = dataGsfSelEff.getEff(vElefinal.Eta(), vElefinal.Pt());
          Double_t effmc   = zeeGsfSelEff.getEff(vElefinal.Eta(), vElefinal.Pt());
          corr *= effdata/effmc;
        }

        if(dataHLTEffFile && zeeHLTEffFile) {
          Double_t effdata = dataHLTEff.getEff(vElefinal.Eta(), vElefinal.Pt());
          Double_t effmc   = zeeHLTEff.getEff(vElefinal.Eta(), vElefinal.Pt());
          Double_t errdata = TMath::Max(dataHLTEff.getErrLow(vElefinal.Eta(), vElefinal.Pt()),dataHLTEff.getErrHigh(vElefinal.Eta(), vElefinal.Pt()));
          Double_t errmc   = TMath::Max(zeeHLTEff.getErrLow(vElefinal.Eta(), vElefinal.Pt()), zeeHLTEff.getErrHigh(vElefinal.Eta(), vElefinal.Pt()));
          Double_t err     = corr*sqrt(errdata*errdata/effdata/effdata+errmc*errmc/effmc/effmc);
          hHLTErr->Fill(vElefinal.Eta(),vElefinal.Pt(),err);
          if(isBarrel) hHLTErrB->Fill(vElefinal.Eta(),vElefinal.Pt(),err);
          else         hHLTErrE->Fill(vElefinal.Eta(),vElefinal.Pt(),err);
        }
        if(dataGsfSelEffFile && zeeGsfSelEffFile) {
          Double_t effdata = dataGsfSelEff.getEff(vElefinal.Eta(), vElefinal.Pt());
          Double_t effmc   = zeeGsfSelEff.getEff(vElefinal.Eta(), vElefinal.Pt());
          Double_t errdata = TMath::Max(dataGsfSelEff.getErrLow(vElefinal.Eta(), vElefinal.Pt()),dataGsfSelEff.getErrHigh(vElefinal.Eta(), vElefinal.Pt()));
          Double_t errmc   = TMath::Max(zeeGsfSelEff.getErrLow(vElefinal.Eta(), vElefinal.Pt()), zeeGsfSelEff.getErrHigh(vElefinal.Eta(), vElefinal.Pt()));
          Double_t err     = corr*sqrt(errdata*errdata/effdata/effdata+errmc*errmc/effmc/effmc);
          hGsfSelErr->Fill(vElefinal.Eta(),vElefinal.Pt(),err);
          if(isBarrel) hGsfSelErrB->Fill(vElefinal.Eta(),vElefinal.Pt(),err);
          else         hGsfSelErrE->Fill(vElefinal.Eta(),vElefinal.Pt(),err);
        }
        
	nSelv[ifile]+=weight;
	nSelCorrv[ifile]+=weight*corr;
	nSelCorrVarv[ifile]+=weight*weight*corr*corr;

  	if(isBarrel) { 
	  nSelBv[ifile]+=weight;
	  nSelBCorrv[ifile]+=weight*corr;
	  nSelBCorrVarv[ifile]+=weight*weight*corr*corr;
	  	
	} else { 
	  nSelEv[ifile]+=weight;
	  nSelECorrv[ifile]+=weight*corr;
	  nSelECorrVarv[ifile]+=weight*weight*corr*corr;
	}
      }
    }
    
    Double_t var=0, varB=0, varE=0;
    for(Int_t iy=0; iy<=hHLTErr->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hHLTErr->GetNbinsX(); ix++) {
        Double_t err;
	err=hHLTErr->GetBinContent(ix,iy);  var+=err*err;
        err=hHLTErrB->GetBinContent(ix,iy); varB+=err*err;
        err=hHLTErrE->GetBinContent(ix,iy); varE+=err*err;
      }
    }

    for(Int_t iy=0; iy<=hGsfSelErr->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hGsfSelErr->GetNbinsX(); ix++) {
        Double_t err;
	err=hGsfSelErr->GetBinContent(ix,iy);  var+=err*err;
	err=hGsfSelErrB->GetBinContent(ix,iy); varB+=err*err;
	err=hGsfSelErrE->GetBinContent(ix,iy); varE+=err*err;
      }
    }

    nSelCorrVarv[ifile]+=var;
    nSelBCorrVarv[ifile]+=varB;
    nSelECorrVarv[ifile]+=varE;
    
    // compute acceptances
    accv.push_back(nSelv[ifile]/nEvtsv[ifile]);   accErrv.push_back(sqrt(accv[ifile]*(1.+accv[ifile])/nEvtsv[ifile]));
    accBv.push_back(nSelBv[ifile]/nEvtsv[ifile]); accErrBv.push_back(sqrt(accBv[ifile]*(1.+accBv[ifile])/nEvtsv[ifile]));
    accEv.push_back(nSelEv[ifile]/nEvtsv[ifile]); accErrEv.push_back(sqrt(accEv[ifile]*(1.+accEv[ifile])/nEvtsv[ifile]));
    
    accCorrv.push_back(nSelCorrv[ifile]/nEvtsv[ifile]);   accErrCorrv.push_back(accCorrv[ifile]*sqrt(nSelCorrVarv[ifile]/nSelCorrv[ifile]/nSelCorrv[ifile] + 1./nEvtsv[ifile]));
    accBCorrv.push_back(nSelBCorrv[ifile]/nEvtsv[ifile]); accErrBCorrv.push_back(accBCorrv[ifile]*sqrt(nSelBCorrVarv[ifile]/nSelBCorrv[ifile]/nSelBCorrv[ifile] + 1./nEvtsv[ifile]));
    accECorrv.push_back(nSelECorrv[ifile]/nEvtsv[ifile]); accErrECorrv.push_back(accECorrv[ifile]*sqrt(nSelECorrVarv[ifile]/nSelECorrv[ifile]/nSelECorrv[ifile] + 1./nEvtsv[ifile]));
    
    delete infile;
    infile=0, eventTree=0;  
  }
  delete info;
  delete gen;
  delete electronArr;
  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  if(charge== 0) cout << " W -> e nu"  << endl;
  if(charge==-1) cout << " W- -> e nu" << endl;
  if(charge== 1) cout << " W+ -> e nu" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  cout << "  Barrel definition: |eta| < " << ETA_BARREL << endl;
  cout << "  Endcap definition: |eta| > " << ETA_ENDCAP << endl;
  cout << endl;
  
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {
    cout << "   ================================================" << endl;
    cout << "    Label: " << labelv[ifile] << endl;
    cout << "     File: " << fnamev[ifile] << endl;
    cout << endl;
    cout << "    *** Acceptance ***" << endl;
    cout << "     barrel: " << setw(12) << nSelBv[ifile] << " / " << nEvtsv[ifile] << " = " << accBv[ifile] << " +/- " << accErrBv[ifile];
    cout << "  ==eff corr==> " << accBCorrv[ifile] << " +/- " << accErrBCorrv[ifile] << endl;
    cout << "     endcap: " << setw(12) << nSelEv[ifile] << " / " << nEvtsv[ifile] << " = " << accEv[ifile] << " +/- " << accErrEv[ifile];
    cout << "  ==eff corr==> " << accECorrv[ifile] << " +/- " << accErrECorrv[ifile] << endl;
    cout << "      total: " << setw(12) << nSelv[ifile]  << " / " << nEvtsv[ifile] << " = " << accv[ifile]  << " +/- " << accErrv[ifile];
    cout << "  ==eff corr==> " << accCorrv[ifile]  << " +/- " << accErrCorrv[ifile] << endl;
    cout << endl;
  }
  
  char txtfname[100];
  sprintf(txtfname,"%s/sel.txt",outputDir.Data());
  ofstream txtfile;
  txtfile.open(txtfname);
  txtfile << "*" << endl;
  txtfile << "* SUMMARY" << endl;
  txtfile << "*--------------------------------------------------" << endl;
  if(charge== 0) txtfile << " W -> e nu"  << endl;
  if(charge==-1) txtfile << " W- -> e nu" << endl;
  if(charge== 1) txtfile << " W+ -> e nu" << endl;
  txtfile << "  pT > " << PT_CUT << endl;
  txtfile << "  |eta| < " << ETA_CUT << endl;
  txtfile << "  Barrel definition: |eta| < " << ETA_BARREL << endl;
  txtfile << "  Endcap definition: |eta| > " << ETA_ENDCAP << endl;
  txtfile << endl;
  
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {
    txtfile << "   ================================================" << endl;
    txtfile << "    Label: " << labelv[ifile] << endl;
    txtfile << "     File: " << fnamev[ifile] << endl;
    txtfile << endl;
    txtfile << "    *** Acceptance ***" << endl;
    txtfile << "     barrel: " << setw(12) << nSelBv[ifile] << " / " << nEvtsv[ifile] << " = " << accBv[ifile] << " +/- " << accErrBv[ifile];
    txtfile << "  ==eff corr==> " << accBCorrv[ifile] << " +/- " << accErrBCorrv[ifile] << endl;
    txtfile << "     endcap: " << setw(12) << nSelEv[ifile] << " / " << nEvtsv[ifile] << " = " << accEv[ifile] << " +/- " << accErrEv[ifile];
    txtfile << "  ==eff corr==> " << accECorrv[ifile] << " +/- " << accErrECorrv[ifile] << endl;
    txtfile << "      total: " << setw(12) << nSelv[ifile]  << " / " << nEvtsv[ifile] << " = " << accv[ifile]  << " +/- " << accErrv[ifile];
    txtfile << "  ==eff corr==> " << accCorrv[ifile]  << " +/- " << accErrCorrv[ifile] << endl;
    txtfile << endl;
  }
  txtfile.close();  
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("computeAccSelWe"); 
}
示例#5
0
void selectZee(const TString conf="zee.conf", // input file
               const TString outputDir=".",   // output directory
	       const Bool_t  doScaleCorr=0    // apply energy scale corrections?
) {
  gBenchmark->Start("selectZee");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t MASS_LOW  = 40;
  const Double_t MASS_HIGH = 200;
  const Double_t PT_CUT    = 10;
  const Double_t ETA_CUT   = 2.5;
  const Double_t ELE_MASS  = 0.000511;
  
  const Double_t ECAL_GAP_LOW  = 1.4442;
  const Double_t ECAL_GAP_HIGH = 1.566;
  
  const Double_t escaleNbins  = 6;
  const Double_t escaleEta[]  = { 0.4,     0.8,     1.2,     1.4442,  2,        2.5 };
  const Double_t escaleCorr[] = { 1.00284, 1.00479, 1.00734, 1.00851, 1.00001,  0.982898 };

  const Int_t BOSON_ID  = 23;
  const Int_t LEPTON_ID = 11;

  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  enum { eEleEle2HLT=1, eEleEle1HLT1L1, eEleEle1HLT, eEleEleNoSel, eEleSC };  // event category enum
  
  vector<TString>  snamev;      // sample name (for output files)  
  vector<CSample*> samplev;     // data/MC samples

  //
  // parse .conf file
  //
  confParse(conf, snamev, samplev);
  const Bool_t hasData = (samplev[0]->fnamev.size()>0);

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  const TString ntupDir = outputDir + TString("/ntuples");
  gSystem->mkdir(ntupDir,kTRUE);
  
  //
  // Declare output ntuple variables
  //
  UInt_t  runNum, lumiSec, evtNum;
  UInt_t  matchGen;
  UInt_t  category;
  UInt_t  npv, npu;
  UInt_t  id_1, id_2;
  Double_t x_1, x_2, xPDF_1, xPDF_2;
  Double_t scalePDF, weightPDF;
  TLorentzVector *genV=0;
  Float_t genVPt, genVPhi, genVy, genVMass;
  Float_t scale1fb;
  Float_t met, metPhi, sumEt, u1, u2;
  Float_t tkMet, tkMetPhi, tkSumEt, tkU1, tkU2;
  Int_t   q1, q2;
  TLorentzVector *dilep=0, *lep1=0, *lep2=0;
  ///// electron specific /////
  Float_t trkIso1, emIso1, hadIso1, trkIso2, emIso2, hadIso2;
  Float_t pfChIso1, pfGamIso1, pfNeuIso1, pfCombIso1, pfChIso2, pfGamIso2, pfNeuIso2, pfCombIso2;
  Float_t sigieie1, hovere1, eoverp1, fbrem1, ecalE1, sigieie2, hovere2, eoverp2, fbrem2, ecalE2;
  Float_t dphi1, deta1, dphi2, deta2;
  Float_t d01, dz1, d02, dz2;
  UInt_t  isConv1, nexphits1, typeBits1, isConv2, nexphits2, typeBits2; 
  TLorentzVector *sc1=0, *sc2=0;
  
  // Data structures to store info from TTrees
  baconhep::TEventInfo *info   = new baconhep::TEventInfo();
  baconhep::TGenEventInfo *gen = new baconhep::TGenEventInfo();
  TClonesArray *genPartArr     = new TClonesArray("baconhep::TGenParticle");
  TClonesArray *electronArr    = new TClonesArray("baconhep::TElectron");
  TClonesArray *scArr          = new TClonesArray("baconhep::TPhoton");
  TClonesArray *pvArr          = new TClonesArray("baconhep::TVertex");
  
  TFile *infile=0;
  TTree *eventTree=0;
  
  //
  // loop over samples
  //  
  for(UInt_t isam=0; isam<samplev.size(); isam++) {
    
    // Assume data sample is first sample in .conf file
    // If sample is empty (i.e. contains no ntuple files), skip to next sample
    if(isam==0 && !hasData) continue;
    
    // Assume signal sample is given name "zee" - flag to store GEN Z kinematics
    Bool_t isSignal = (snamev[isam].CompareTo("zee",TString::kIgnoreCase)==0);  
    // flag to reject Z->ee events for wrong flavor backgrounds
    Bool_t isWrongFlavor = (snamev[isam].CompareTo("zxx",TString::kIgnoreCase)==0);  
    
    CSample* samp = samplev[isam];
  
    //
    // Set up output ntuple
    //
    TString outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.root");
    if(isam==0 && !doScaleCorr) outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.raw.root");
    TFile *outFile = new TFile(outfilename,"RECREATE"); 
    TTree *outTree = new TTree("Events","Events");
    outTree->Branch("runNum",     &runNum,     "runNum/i");      // event run number
    outTree->Branch("lumiSec",    &lumiSec,    "lumiSec/i");     // event lumi section
    outTree->Branch("evtNum",     &evtNum,     "evtNum/i");      // event number
    outTree->Branch("matchGen",   &matchGen,   "matchGen/i");    // event has both leptons matched to MC Z->ll
    outTree->Branch("category",   &category,   "category/i");    // dilepton category
    outTree->Branch("id_1",       &id_1,       "id_1/i");        // PDF info -- parton ID for parton 1
    outTree->Branch("id_2",       &id_2,       "id_2/i");        // PDF info -- parton ID for parton 2
    outTree->Branch("x_1",        &x_1,        "x_1/d");         // PDF info -- x for parton 1
    outTree->Branch("x_2",        &x_2,        "x_2/d");         // PDF info -- x for parton 2
    outTree->Branch("xPDF_1",     &xPDF_1,     "xPDF_1/d");      // PDF info -- x*F for parton 1
    outTree->Branch("xPDF_2",     &xPDF_2,     "xPDF_2/d");      // PDF info -- x*F for parton 2
    outTree->Branch("scalePDF",   &scalePDF,   "scalePDF/d");    // PDF info -- energy scale of parton interaction
    outTree->Branch("weightPDF",  &weightPDF,  "weightPDF/d");   // PDF info -- PDF weight
    outTree->Branch("npv",        &npv,        "npv/i");         // number of primary vertices
    outTree->Branch("npu",        &npu,        "npu/i");         // number of in-time PU events (MC)
    outTree->Branch("genV",      "TLorentzVector",  &genV);      // GEN boson 4-vector
    outTree->Branch("genVPt",     &genVPt,     "genVPt/F");      // GEN boson pT (signal MC)
    outTree->Branch("genVPhi",    &genVPhi,    "genVPhi/F");     // GEN boson phi (signal MC)
    outTree->Branch("genVy",      &genVy,      "genVy/F");       // GEN boson rapidity (signal MC)
    outTree->Branch("genVMass",   &genVMass,   "genVMass/F");    // GEN boson mass (signal MC)
    outTree->Branch("scale1fb",   &scale1fb,   "scale1fb/F");    // event weight per 1/fb (MC)
    outTree->Branch("met",        &met,        "met/F");         // MET
    outTree->Branch("metPhi",     &metPhi,     "metPhi/F");      // phi(MET)
    outTree->Branch("sumEt",      &sumEt,      "sumEt/F");       // Sum ET
    outTree->Branch("u1",         &u1,         "u1/F");          // parallel component of recoil
    outTree->Branch("u2",         &u2,         "u2/F");          // perpendicular component of recoil
    outTree->Branch("tkMet",      &tkMet,      "tkMet/F");       // MET (track MET)
    outTree->Branch("tkMetPhi",   &tkMetPhi,   "tkMetPhi/F");    // phi(MET) (track MET)
    outTree->Branch("tkSumEt",    &tkSumEt,    "tkSumEt/F");     // Sum ET (track MET)
    outTree->Branch("tkU1",       &tkU1,       "tkU1/F");        // parallel component of recoil (track MET)
    outTree->Branch("tkU2",       &tkU2,       "tkU2/F");        // perpendicular component of recoil (track MET)
    outTree->Branch("q1",         &q1,         "q1/I");          // charge of tag lepton
    outTree->Branch("q2",         &q2,         "q2/I");          // charge of probe lepton
    outTree->Branch("dilep",      "TLorentzVector",  &dilep);    // di-lepton 4-vector
    outTree->Branch("lep1",       "TLorentzVector",  &lep1);     // tag lepton 4-vector
    outTree->Branch("lep2",       "TLorentzVector",  &lep2);     // probe lepton 4-vector
    ///// electron specific /////
    outTree->Branch("trkIso1",    &trkIso1,    "trkIso1/F");     // track isolation of tag lepton
    outTree->Branch("trkIso2",    &trkIso2,    "trkIso2/F");     // track isolation of probe lepton
    outTree->Branch("emIso1",     &emIso1,     "emIso1/F");      // ECAL isolation of tag lepton
    outTree->Branch("emIso2",     &emIso2,     "emIso2/F");      // ECAL isolation of probe lepton
    outTree->Branch("hadIso1",    &hadIso1,    "hadIso1/F");     // HCAL isolation of tag lepton
    outTree->Branch("hadIso2",    &hadIso2,    "hadIso2/F");     // HCAL isolation of probe lepton
    outTree->Branch("pfChIso1",   &pfChIso1,   "pfChIso1/F");    // PF charged hadron isolation of tag lepton
    outTree->Branch("pfChIso2",   &pfChIso2,   "pfChIso2/F");    // PF charged hadron isolation of probe lepton
    outTree->Branch("pfGamIso1",  &pfGamIso1,  "pfGamIso1/F");   // PF photon isolation of tag lepton
    outTree->Branch("pfGamIso2",  &pfGamIso2,  "pfGamIso2/F");   // PF photon isolation of probe lepton
    outTree->Branch("pfNeuIso1",  &pfNeuIso1,  "pfNeuIso1/F");   // PF neutral hadron isolation of tag lepton
    outTree->Branch("pfNeuIso2",  &pfNeuIso2,  "pfNeuIso2/F");   // PF neutral hadron isolation of probe lepton
    outTree->Branch("pfCombIso1", &pfCombIso1, "pfCombIso1/F");  // PF combine isolation of tag lepton
    outTree->Branch("pfCombIso2", &pfCombIso2, "pfCombIso2/F");  // PF combined isolation of probe lepton    
    outTree->Branch("sigieie1",   &sigieie1,   "sigieie1/F");    // sigma-ieta-ieta of tag
    outTree->Branch("sigieie2",   &sigieie2,   "sigieie2/F");    // sigma-ieta-ieta of probe
    outTree->Branch("hovere1",    &hovere1,    "hovere1/F");     // H/E of tag
    outTree->Branch("hovere2",    &hovere2,    "hovere2/F");     // H/E of probe
    outTree->Branch("eoverp1",    &eoverp1,    "eoverp1/F");     // E/p of tag
    outTree->Branch("eoverp2",    &eoverp2,    "eoverp2/F");     // E/p of probe	 
    outTree->Branch("fbrem1",     &fbrem1,     "fbrem1/F");      // brem fraction of tag
    outTree->Branch("fbrem2",     &fbrem2,     "fbrem2/F");      // brem fraction of probe
    outTree->Branch("dphi1",      &dphi1,      "dphi1/F");       // GSF track - ECAL dphi of tag
    outTree->Branch("dphi2",      &dphi2,      "dphi2/F");       // GSF track - ECAL dphi of probe 	
    outTree->Branch("deta1",      &deta1,      "deta1/F");       // GSF track - ECAL deta of tag
    outTree->Branch("deta2",      &deta2,      "deta2/F");       // GSF track - ECAL deta of probe
    outTree->Branch("ecalE1",     &ecalE1,     "ecalE1/F");      // ECAL energy of tag
    outTree->Branch("ecalE2",     &ecalE2,     "ecalE2/F");      // ECAL energy of probe
    outTree->Branch("d01",        &d01,        "d01/F");	 // transverse impact parameter of tag
    outTree->Branch("d02",        &d02,        "d02/F");	 // transverse impact parameter of probe	  
    outTree->Branch("dz1",        &dz1,        "dz1/F");	 // longitudinal impact parameter of tag
    outTree->Branch("dz2",        &dz2,        "dz2/F");	 // longitudinal impact parameter of probe
    outTree->Branch("isConv1",    &isConv1,    "isConv1/i");     // conversion filter flag of tag lepton
    outTree->Branch("isConv2",    &isConv2,    "isConv2/i");     // conversion filter flag of probe lepton
    outTree->Branch("nexphits1",  &nexphits1,  "nexphits1/i");   // number of missing expected inner hits of tag lepton
    outTree->Branch("nexphits2",  &nexphits2,  "nexphits2/i");   // number of missing expected inner hits of probe lepton
    outTree->Branch("typeBits1",  &typeBits1,  "typeBits1/i");   // electron type of tag lepton
    outTree->Branch("typeBits2",  &typeBits2,  "typeBits2/i");   // electron type of probe lepton
    outTree->Branch("sc1",       "TLorentzVector",  &sc1);       // tag supercluster 4-vector
    outTree->Branch("sc2",       "TLorentzVector",  &sc2);       // probe supercluster 4-vector

    //
    // loop through files
    //
    const UInt_t nfiles = samp->fnamev.size();
    for(UInt_t ifile=0; ifile<nfiles; ifile++) {  

      // Read input file and get the TTrees
      cout << "Processing " << samp->fnamev[ifile] << " [xsec = " << samp->xsecv[ifile] << " pb] ... " << endl; cout.flush();
      infile = TFile::Open(samp->fnamev[ifile]); 
      assert(infile);

      const baconhep::TTrigger triggerMenu("../../BaconAna/DataFormats/data/HLT_50nsGRun");
      UInt_t trigger    = triggerMenu.getTriggerBit("HLT_Ele23_WP75_Gsf_v*");
      //need to clean this up
      UInt_t trigObjL1  = 4;//triggerMenu.getTriggerObjectBit("HLT_Ele22_WP75_Gsf_v*", "hltL1sL1SingleEG20");
      UInt_t trigObjHLT = 5;//triggerMenu.getTriggerObjectBit("HLT_Ele23_WP75_Gsf_v*", "hltEle23WP75GsfTrackIsoFilter");

      /*      cout << endl << "Checking trigger bits: " << endl;
      cout << "HLT_Ele22_WP75_Gsf_v*         " << triggerMenu.getTriggerBit("HLT_Ele22_WP75_Gsf_v*") << endl;
      cout << "HLT_Ele23_WP75_Gsf_v*         " << triggerMenu.getTriggerBit("HLT_Ele23_WP75_Gsf_v*") << endl;
      cout << "HLT_IsoMu20_v*                " << triggerMenu.getTriggerBit("HLT_IsoMu20_v*")        << endl;

      cout << "trigObjL1     " << trigObjL1 << endl;
      cout << "trigObjHLT    " << trigObjHLT << endl;*/

      //Bool_t hasJSON = kFALSE;
      //baconhep::RunLumiRangeMap rlrm;
      //if(samp->jsonv[ifile].CompareTo("NONE")!=0) { 
      //hasJSON = kTRUE;
      //rlrm.AddJSONFile(samp->jsonv[ifile].Data()); 
      //}
  
      eventTree = (TTree*)infile->Get("Events");
      assert(eventTree);  
      eventTree->SetBranchAddress("Info",     &info);        TBranch *infoBr     = eventTree->GetBranch("Info");
      eventTree->SetBranchAddress("Electron", &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");
      eventTree->SetBranchAddress("Photon",   &scArr);       TBranch *scBr       = eventTree->GetBranch("Photon");
      Bool_t hasGen = eventTree->GetBranchStatus("GenEvtInfo");
      TBranch *genBr=0, *genPartBr=0;
      if(hasGen) {
        eventTree->SetBranchAddress("GenEvtInfo", &gen); genBr = eventTree->GetBranch("GenEvtInfo");
	eventTree->SetBranchAddress("GenParticle",&genPartArr); genPartBr = eventTree->GetBranch("GenParticle");
      }

      Bool_t hasVer = eventTree->GetBranchStatus("Vertex");
      TBranch *pvBr=0;
      if (hasVer) {
	eventTree->SetBranchAddress("Vertex", &pvArr); pvBr = eventTree->GetBranch("Vertex");
      }

      // Compute MC event weight per 1/fb
      Double_t weight = 1;
      const Double_t xsec = samp->xsecv[ifile];
      if(xsec>0) weight = 1000.*xsec/(Double_t)eventTree->GetEntries();     

      //
      // loop over events
      //
      Double_t nsel=0, nselvar=0;
      //for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
      for(UInt_t ientry=0; ientry<1000; ientry++) {
        infoBr->GetEntry(ientry);
	
	if(hasGen) {
	  genBr->GetEntry(ientry);
	  genPartArr->Clear();
	  genPartBr->GetEntry(ientry);
	}
     
        // check for certified lumi (if applicable)
        //baconhep::RunLumiRangeMap::RunLumiPairType rl(info->runNum, info->lumiSec);      
        //if(hasJSON && !rlrm.HasRunLumi(rl)) continue;  

        // trigger requirement               
        if(!(info->triggerBits[trigger])) continue;
      
        // good vertex requirement
        if(!(info->hasGoodPV)) continue;
	if (hasVer) {
	  pvArr->Clear();
	  pvBr->GetEntry(ientry);
	}

        //
	// SELECTION PROCEDURE:
	//  (1) Find a good electron matched to trigger -> this will be the "tag"
	//  (2) Pair the tag with Supercluster probes which form a tag+probe mass inside 
	//      the Z window and divide candidates into exclusive categories as follows:
	//      (a) if probe SC is part of a good electron matched to trigger     -> EleEle2HLT category
	//      (b) if probe SC is part of a good electron not matched to trigger -> EleEle1HLT category
	//      (c) if probe SC is part of an electron failing selection cuts     -> EleEleNoSel category
	//      (d) if probe SC is not part of an ECAL driven electron            -> EleSC category
	//	
	electronArr->Clear();
        electronBr->GetEntry(ientry);
	scArr->Clear();
	scBr->GetEntry(ientry);
        for(Int_t i1=0; i1<electronArr->GetEntriesFast(); i1++) {
          const baconhep::TElectron *tag = (baconhep::TElectron*)((*electronArr)[i1]);
	  
	  // check ECAL gap
	  if(fabs(tag->scEta)>=ECAL_GAP_LOW && fabs(tag->scEta)<=ECAL_GAP_HIGH) continue;
	  
	  Double_t escale1=1;
	  if(doScaleCorr && isam==0) {
	    for(UInt_t ieta=0; ieta<escaleNbins; ieta++) {
	      if(fabs(tag->scEta)<escaleEta[ieta]) {
	        escale1 = escaleCorr[ieta];
		break;
	      }
	    }
	  }
	  if(escale1*(tag->scEt) < PT_CUT)     continue;  // lepton pT cut
	  if(fabs(tag->scEta)    > ETA_CUT)    continue;  // lepton |eta| cut
	  if(!passEleID(tag,info->rhoIso))     continue;  // lepton selection
	  if(!(tag->hltMatchBits[trigObjHLT])) continue;  // check trigger matching

	  TLorentzVector vTag;     vTag.SetPtEtaPhiM(escale1*(tag->pt),   tag->eta,   tag->phi,   ELE_MASS);
	  TLorentzVector vTagSC; vTagSC.SetPtEtaPhiM(escale1*(tag->scEt), tag->scEta, tag->scPhi, ELE_MASS);
	
	  for(Int_t j=0; j<scArr->GetEntriesFast(); j++) {
	    const baconhep::TPhoton *scProbe = (baconhep::TPhoton*)((*scArr)[j]);
	    if(scProbe->scID == tag->scID) continue;

	    // check ECAL gap
	    if(fabs(scProbe->scEta)>=ECAL_GAP_LOW && fabs(scProbe->scEta)<=ECAL_GAP_HIGH) continue;
	    
	    Double_t escale2=1;
	    if(doScaleCorr && isam==0) {
	      for(UInt_t ieta=0; ieta<escaleNbins; ieta++) {
	        if(fabs(scProbe->scEta)<escaleEta[ieta]) {
	          escale2 = escaleCorr[ieta];
		  break;
	        }
	      }
	    }
	    
	    if(escale2*(scProbe->pt) < PT_CUT)  continue;  // Supercluster ET cut ("pt" = corrected by PV position)
	    if(fabs(scProbe->scEta)  > ETA_CUT) continue;  // Supercluster |eta| cuts
	    
	    const baconhep::TElectron *eleProbe=0;
	    Int_t iprobe=-1;
	    for(Int_t i2=0; i2<electronArr->GetEntriesFast(); i2++) {
	      if(i1==i2) continue;
	      const baconhep::TElectron *ele = (baconhep::TElectron*)((*electronArr)[i2]);
	      if(!(ele->typeBits & baconhep::EEleType::kEcalDriven)) continue;
	      if(scProbe->scID==ele->scID) { 
	        eleProbe = ele; 
		iprobe   = i2;
		break; 
	      }
            }

	    TLorentzVector vProbe(0,0,0,0);
	    vProbe.SetPtEtaPhiM((eleProbe) ? escale2*(eleProbe->pt)  : escale2*(scProbe->pt),
				(eleProbe) ? eleProbe->eta : scProbe->eta,
				(eleProbe) ? eleProbe->phi : scProbe->phi,
				ELE_MASS);
	    TLorentzVector vProbeSC(0,0,0,0);
	    vProbeSC.SetPtEtaPhiM((eleProbe) ? escale2*(eleProbe->scEt)  : escale2*(scProbe->scEt),
				  scProbe->scEta, scProbe->scPhi, ELE_MASS);

	    // mass window
	    TLorentzVector vDilep = vTag + vProbe;
	    if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;

	    //only for looking at low pT trigger efficiencies
	    //if (toolbox::deltaR(vTag.Eta(), vProbe.Eta(), vTag.Phi(), vProbe.Phi())<0.3) continue;

	    // determine event category
	    UInt_t icat=0;
	    if(eleProbe) {
	      if(passEleID(eleProbe,info->rhoIso)) {

	        if(eleProbe->hltMatchBits[trigObjHLT]) {
		  if(i1>iprobe) continue;  // make sure we don't double count EleEle2HLT category
		  icat=eEleEle2HLT;  
		} 
		else if (eleProbe->hltMatchBits[trigObjL1]) { icat=eEleEle1HLT1L1; }
		else { icat=eEleEle1HLT; }
	      }
	      else { icat=eEleEleNoSel; } 
	    } 
	    else { icat=eEleSC; }

	    if(icat==0) continue;

	    // veto z -> ee decay for wrong flavor background samples (needed for inclusive DYToLL sample)
	    if (isWrongFlavor) {
	      TLorentzVector *vec=0, *lep1=0, *lep2=0;
	      if (fabs(toolbox::flavor(genPartArr, BOSON_ID, vec, lep1, lep2))==LEPTON_ID) continue;
	    }

	    /******** We have a Z candidate! HURRAY! ********/
	    nsel+=weight;
            nselvar+=weight*weight;

	    // Perform matching of dileptons to GEN leptons from Z decay
	    Bool_t hasGenMatch = kFALSE;
	    if(isSignal && hasGen) {
	      TLorentzVector *vec=0, *lep1=0, *lep2=0;
	      // veto wrong flavor events for signal sample
	      if (fabs(toolbox::flavor(genPartArr, BOSON_ID, vec, lep1, lep2))!=LEPTON_ID) continue;
	      Bool_t match1 = ( ((lep1) && toolbox::deltaR(tag->eta, tag->phi, lep1->Eta(), lep1->Phi())<0.3) || 
				((lep2) && toolbox::deltaR(tag->eta, tag->phi, lep2->Eta(), lep2->Phi())<0.3) );

	      Bool_t match2 = ( ((lep1) && toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), lep1->Eta(), lep1->Phi())<0.3) || 
				((lep2) && toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), lep2->Eta(), lep2->Phi())<0.3) );
	      if(match1 && match2) {
		hasGenMatch = kTRUE;
		if (vec!=0) {
		  genV=new TLorentzVector(0,0,0,0);
		  genV->SetPtEtaPhiM(vec->Pt(), vec->Eta(), vec->Phi(), vec->M());
		  genVPt   = vec->Pt();
		  genVPhi  = vec->Phi();
		  genVy    = vec->Rapidity();
		  genVMass = vec->M();
		}
		else {
		  TLorentzVector tvec=*lep1+*lep2;
		  genV=new TLorentzVector(0,0,0,0);
		  genV->SetPtEtaPhiM(tvec.Pt(), tvec.Eta(), tvec.Phi(), tvec.M());
		  genVPt   = tvec.Pt();
		  genVPhi  = tvec.Phi();
		  genVy    = tvec.Rapidity();
		  genVMass = tvec.M();
		}
	      }
	      else {
		genV     = new TLorentzVector(0,0,0,0);
		genVPt   = -999;
		genVPhi  = -999;
		genVy    = -999;
		genVMass = -999;
	      }
	    }
	    
	    if (hasGen) {
	      id_1      = gen->id_1;
	      id_2      = gen->id_2;
	      x_1       = gen->x_1;
	      x_2       = gen->x_2;
	      xPDF_1    = gen->xPDF_1;
	      xPDF_2    = gen->xPDF_2;
	      scalePDF  = gen->scalePDF;
	      weightPDF = gen->weight;
	    }
	    else {
	      id_1      = -999;
	      id_2      = -999;
	      x_1       = -999;
	      x_2       = -999;
	      xPDF_1    = -999;
	      xPDF_2    = -999;
	      scalePDF  = -999;
	      weightPDF = -999;
	    }

	    //
	    // Fill tree
	    //
	    runNum   = info->runNum;
	    lumiSec  = info->lumiSec;
	    evtNum   = info->evtNum;

	    if (hasGenMatch) matchGen=1;
            else matchGen=0;

	    category = icat;
	    npv      = hasVer ? pvArr->GetEntriesFast() : 0;
	    npu      = info->nPU;
	    scale1fb = weight;
	    met      = info->pfMET;
	    metPhi   = info->pfMETphi;
	    sumEt    = 0;
	    tkMet    = info->trkMET;
	    tkMetPhi = info->trkMETphi;
	    tkSumEt  = 0;
	    lep1     = &vTag;
	    lep2     = &vProbe;
	    dilep    = &vDilep;
	    q1       = tag->q;
	    q2       = (eleProbe) ? eleProbe->q : -(tag->q);

	    TVector2 vZPt((vDilep.Pt())*cos(vDilep.Phi()),(vDilep.Pt())*sin(vDilep.Phi()));        
            TVector2 vMet((info->pfMET)*cos(info->pfMETphi), (info->pfMET)*sin(info->pfMETphi));        
            TVector2 vU = -1.0*(vMet+vZPt);
            u1 = ((vDilep.Px())*(vU.Px()) + (vDilep.Py())*(vU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
            u2 = ((vDilep.Px())*(vU.Py()) - (vDilep.Py())*(vU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|

            TVector2 vTkMet((info->trkMET)*cos(info->trkMETphi), (info->trkMET)*sin(info->trkMETphi));        
            TVector2 vTkU = -1.0*(vTkMet+vZPt);
            tkU1 = ((vDilep.Px())*(vTkU.Px()) + (vDilep.Py())*(vTkU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
            tkU2 = ((vDilep.Px())*(vTkU.Py()) - (vDilep.Py())*(vTkU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|
	  
	    ///// electron specific ///// 
	    sc1        = &vTagSC;
	    trkIso1    = tag->trkIso;
	    emIso1     = tag->ecalIso;
	    hadIso1    = tag->hcalIso;
	    pfChIso1   = tag->chHadIso;
	    pfGamIso1  = tag->gammaIso;	    
	    pfNeuIso1  = tag->neuHadIso;
	    pfCombIso1 = tag->chHadIso + TMath::Max(tag->neuHadIso + tag->gammaIso - 
						    (info->rhoIso)*getEffArea(tag->scEta), 0.);
	    sigieie1   = tag->sieie;
	    hovere1    = tag->hovere;
	    eoverp1    = tag->eoverp;
	    fbrem1     = tag->fbrem;
	    dphi1      = tag->dPhiIn;
	    deta1      = tag->dEtaIn;
	    ecalE1     = tag->ecalEnergy;
	    d01        = tag->d0;
	    dz1        = tag->dz;
	    isConv1    = tag->isConv;
	    nexphits1  = tag->nMissingHits;
	    typeBits1  = tag->typeBits;

	    sc2        = &vProbeSC;
	    trkIso2    = (eleProbe) ? eleProbe->trkIso        : -1;
	    emIso2     = (eleProbe) ? eleProbe->ecalIso       : -1;
	    hadIso2    = (eleProbe) ? eleProbe->hcalIso       : -1;
	    pfChIso2   = (eleProbe) ? eleProbe->chHadIso      : -1;
	    pfGamIso2  = (eleProbe) ? eleProbe->gammaIso      : -1;
	    pfNeuIso2  = (eleProbe) ? eleProbe->neuHadIso     : -1;	    
	    pfCombIso2 = (eleProbe) ? 
	      eleProbe->chHadIso + TMath::Max(eleProbe->neuHadIso + eleProbe->gammaIso - 
					      (info->rhoIso)*getEffArea(eleProbe->scEta), 0.) :  -1;
	    sigieie2   = (eleProbe) ? eleProbe->sieie         : scProbe->sieie;
	    hovere2    = (eleProbe) ? eleProbe->hovere        : scProbe->hovere;
	    eoverp2    = (eleProbe) ? eleProbe->eoverp        : -1;
	    fbrem2     = (eleProbe) ? eleProbe->fbrem         : -1;
	    dphi2      = (eleProbe) ? eleProbe->dPhiIn        : -999;
	    deta2      = (eleProbe) ? eleProbe->dEtaIn        : -999;
	    ecalE2     = (eleProbe) ? eleProbe->ecalEnergy    : -999;
	    d02        = (eleProbe) ? eleProbe->d0            : -999;
	    dz2        = (eleProbe) ? eleProbe->dz            : -999;
	    isConv2    = (eleProbe) ? eleProbe->isConv        : 0;
	    nexphits2  = (eleProbe) ? eleProbe->nMissingHits  : 0;
	    typeBits2  = (eleProbe) ? eleProbe->typeBits      : 0; 

	    outTree->Fill();
	    genV=0, dilep=0, lep1=0, lep2=0, sc1=0, sc2=0;
	  }
        }
      }
      delete infile;
      infile=0, eventTree=0;    

      cout << nsel  << " +/- " << sqrt(nselvar);
      if(isam!=0) cout << " per 1/fb";
      cout << endl;
    }
    outFile->Write();
    outFile->Close(); 
  }
  delete info;
  delete gen;
  delete electronArr;
  delete scArr;
  delete pvArr;
  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> e e" << endl;
  cout << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  if(doScaleCorr)
    cout << "  *** Scale corrections applied ***" << endl;
  cout << endl;
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("selectZee"); 
}
示例#6
0
void selectZee(const TString conf="zee.conf", // input file
               const TString outputDir=".",   // output directory
	       const Bool_t  doScaleCorr=0    // apply energy scale corrections?
) {
  gBenchmark->Start("selectZee");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t MASS_LOW  = 40;
  const Double_t MASS_HIGH = 200;
  const Double_t PT_CUT    = 22;
  const Double_t ETA_CUT   = 2.5;
  const Double_t ELE_MASS  = 0.000511;
  
  const Double_t ECAL_GAP_LOW  = 1.4442;
  const Double_t ECAL_GAP_HIGH = 1.566;

  const Double_t escaleNbins  = 2;
  const Double_t escaleEta[]  = { 1.4442, 2.5   };
  const Double_t escaleCorr[] = { 0.992,  1.009 };

  const Int_t BOSON_ID  = 23;
  const Int_t LEPTON_ID = 11;

  // load trigger menu
  const baconhep::TTrigger triggerMenu("../../BaconAna/DataFormats/data/HLT_50nsGRun");

  // load pileup reweighting file
  TFile *f_rw = TFile::Open("../Tools/pileup_rw_76X.root", "read");

  // for systematics we need 3
  TH1D *h_rw = (TH1D*) f_rw->Get("h_rw_golden");
  TH1D *h_rw_up = (TH1D*) f_rw->Get("h_rw_up_golden");
  TH1D *h_rw_down = (TH1D*) f_rw->Get("h_rw_down_golden");

  if (h_rw==NULL) cout<<"WARNIG h_rw == NULL"<<endl;
  if (h_rw_up==NULL) cout<<"WARNIG h_rw == NULL"<<endl;
  if (h_rw_down==NULL) cout<<"WARNIG h_rw == NULL"<<endl;

  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  enum { eEleEle2HLT=1, eEleEle1HLT1L1, eEleEle1HLT, eEleEleNoSel, eEleSC };  // event category enum
  
  vector<TString>  snamev;      // sample name (for output files)  
  vector<CSample*> samplev;     // data/MC samples

  //
  // parse .conf file
  //
  confParse(conf, snamev, samplev);
  const Bool_t hasData = (samplev[0]->fnamev.size()>0);

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  const TString ntupDir = outputDir + TString("/ntuples");
  gSystem->mkdir(ntupDir,kTRUE);
  
  //
  // Declare output ntuple variables
  //
  UInt_t  runNum, lumiSec, evtNum;
  UInt_t  matchGen;
  UInt_t  category;
  UInt_t  npv, npu;
  UInt_t  id_1, id_2;
  Double_t x_1, x_2, xPDF_1, xPDF_2;
  Double_t scalePDF, weightPDF;
  TLorentzVector *genV=0;
  Float_t genVPt, genVPhi, genVy, genVMass;
  Float_t genWeight, PUWeight;
  Float_t scale1fb,scale1fbUp,scale1fbDown;
  Float_t met, metPhi, sumEt, u1, u2;
  Float_t tkMet, tkMetPhi, tkSumEt, tkU1, tkU2;
  Float_t mvaMet, mvaMetPhi, mvaSumEt, mvaU1, mvaU2;
  Float_t puppiMet, puppiMetPhi, puppiSumEt, puppiU1, puppiU2;
  Int_t   q1, q2;
  TLorentzVector *dilep=0, *lep1=0, *lep2=0;
  ///// electron specific /////
  Float_t trkIso1, emIso1, hadIso1, trkIso2, emIso2, hadIso2;
  Float_t pfChIso1, pfGamIso1, pfNeuIso1, pfCombIso1, pfChIso2, pfGamIso2, pfNeuIso2, pfCombIso2;
  Float_t sigieie1, hovere1, eoverp1, fbrem1, ecalE1, sigieie2, hovere2, eoverp2, fbrem2, ecalE2;
  Float_t dphi1, deta1, dphi2, deta2;
  Float_t d01, dz1, d02, dz2;
  UInt_t  isConv1, nexphits1, typeBits1, isConv2, nexphits2, typeBits2; 
  TLorentzVector *sc1=0, *sc2=0;
  
  // Data structures to store info from TTrees
  baconhep::TEventInfo *info   = new baconhep::TEventInfo();
  baconhep::TGenEventInfo *gen = new baconhep::TGenEventInfo();
  TClonesArray *genPartArr     = new TClonesArray("baconhep::TGenParticle");
  TClonesArray *electronArr    = new TClonesArray("baconhep::TElectron");
  TClonesArray *scArr          = new TClonesArray("baconhep::TPhoton");
  TClonesArray *vertexArr      = new TClonesArray("baconhep::TVertex");

  TFile *infile=0;
  TTree *eventTree=0;
  
  //
  // loop over samples
  //  
  for(UInt_t isam=0; isam<samplev.size(); isam++) {
    
    // Assume data sample is first sample in .conf file
    // If sample is empty (i.e. contains no ntuple files), skip to next sample
    Bool_t isData=kFALSE;
    if(isam==0 && !hasData) continue;
    else if (isam==0) isData=kTRUE;
    
    // Assume signal sample is given name "zee" - flag to store GEN Z kinematics
    Bool_t isSignal = (snamev[isam].CompareTo("zee",TString::kIgnoreCase)==0);  
    // flag to reject Z->ee events when selecting at wrong-flavor background events
    Bool_t isWrongFlavor = (snamev[isam].CompareTo("zxx",TString::kIgnoreCase)==0);  
    
    CSample* samp = samplev[isam];
  
    //
    // Set up output ntuple
    //
    TString outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.root");
    if(isam!=0 && !doScaleCorr) outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.raw.root");
    TFile *outFile = new TFile(outfilename,"RECREATE"); 
    TTree *outTree = new TTree("Events","Events");
    outTree->Branch("runNum",     &runNum,     "runNum/i");      // event run number
    outTree->Branch("lumiSec",    &lumiSec,    "lumiSec/i");     // event lumi section
    outTree->Branch("evtNum",     &evtNum,     "evtNum/i");      // event number
    outTree->Branch("matchGen",   &matchGen,   "matchGen/i");    // event has both leptons matched to MC Z->ll
    outTree->Branch("category",   &category,   "category/i");    // dilepton category
    outTree->Branch("id_1",       &id_1,       "id_1/i");        // PDF info -- parton ID for parton 1
    outTree->Branch("id_2",       &id_2,       "id_2/i");        // PDF info -- parton ID for parton 2
    outTree->Branch("x_1",        &x_1,        "x_1/d");         // PDF info -- x for parton 1
    outTree->Branch("x_2",        &x_2,        "x_2/d");         // PDF info -- x for parton 2
    outTree->Branch("xPDF_1",     &xPDF_1,     "xPDF_1/d");      // PDF info -- x*F for parton 1
    outTree->Branch("xPDF_2",     &xPDF_2,     "xPDF_2/d");      // PDF info -- x*F for parton 2
    outTree->Branch("scalePDF",   &scalePDF,   "scalePDF/d");    // PDF info -- energy scale of parton interaction
    outTree->Branch("weightPDF",  &weightPDF,  "weightPDF/d");   // PDF info -- PDF weight
    outTree->Branch("npv",        &npv,        "npv/i");         // number of primary vertices
    outTree->Branch("npu",        &npu,        "npu/i");         // number of in-time PU events (MC)
    outTree->Branch("genV",      "TLorentzVector",  &genV);      // GEN boson 4-vector
    outTree->Branch("genVPt",     &genVPt,     "genVPt/F");      // GEN boson pT (signal MC)
    outTree->Branch("genVPhi",    &genVPhi,    "genVPhi/F");     // GEN boson phi (signal MC)
    outTree->Branch("genVy",      &genVy,      "genVy/F");       // GEN boson rapidity (signal MC)
    outTree->Branch("genVMass",   &genVMass,   "genVMass/F");    // GEN boson mass (signal MC)
    outTree->Branch("genWeight",   &genWeight,  "genWeight/F");
    outTree->Branch("PUWeight",    &PUWeight,   "PUWeight/F");
    outTree->Branch("scale1fb",   &scale1fb,   "scale1fb/F");    // event weight per 1/fb (MC)
    outTree->Branch("scale1fbUp",    &scale1fbUp,   "scale1fbUp/F");    // event weight per 1/fb (MC)
    outTree->Branch("scale1fbDown",    &scale1fbDown,   "scale1fbDown/F");    // event weight per 1/fb (MC)
    outTree->Branch("met",        &met,        "met/F");         // MET
    outTree->Branch("metPhi",     &metPhi,     "metPhi/F");      // phi(MET)
    outTree->Branch("sumEt",      &sumEt,      "sumEt/F");       // Sum ET
    outTree->Branch("u1",         &u1,         "u1/F");          // parallel component of recoil
    outTree->Branch("u2",         &u2,         "u2/F");          // perpendicular component of recoil
    outTree->Branch("tkMet",      &tkMet,      "tkMet/F");       // MET (track MET)
    outTree->Branch("tkMetPhi",   &tkMetPhi,   "tkMetPhi/F");    // phi(MET) (track MET)
    outTree->Branch("tkSumEt",    &tkSumEt,    "tkSumEt/F");     // Sum ET (track MET)
    outTree->Branch("tkU1",       &tkU1,       "tkU1/F");        // parallel component of recoil (track MET)
    outTree->Branch("tkU2",       &tkU2,       "tkU2/F");        // perpendicular component of recoil (track MET)
    outTree->Branch("mvaMet",     &mvaMet,     "mvaMet/F");      // MVA MET
    outTree->Branch("mvaMetPhi",  &mvaMetPhi,  "mvaMetPhi/F");   // phi(MVA MET)
    outTree->Branch("mvaSumEt",   &mvaSumEt,   "mvaSumEt/F");    // Sum ET (mva MET)
    outTree->Branch("mvaU1",      &mvaU1,      "mvaU1/F");       // parallel component of recoil (mva MET)
    outTree->Branch("mvaU2",      &mvaU2,      "mvaU2/F");       // perpendicular component of recoil (mva MET)
    outTree->Branch("puppiMet",    &puppiMet,   "puppiMet/F");      // Puppi MET
    outTree->Branch("puppiMetPhi", &puppiMetPhi,"puppiMetPhi/F");   // phi(Puppi MET)
    outTree->Branch("puppiSumEt",  &puppiSumEt, "puppiSumEt/F");    // Sum ET (Puppi MET)
    outTree->Branch("puppiU1",     &puppiU1,    "puppiU1/F");       // parallel component of recoil (Puppi MET)
    outTree->Branch("puppiU2",     &puppiU2,    "puppiU2/F");       // perpendicular component of recoil (Puppi MET)
    outTree->Branch("q1",         &q1,         "q1/I");          // charge of tag lepton
    outTree->Branch("q2",         &q2,         "q2/I");          // charge of probe lepton
    outTree->Branch("dilep",      "TLorentzVector",  &dilep);    // di-lepton 4-vector
    outTree->Branch("lep1",       "TLorentzVector",  &lep1);     // tag lepton 4-vector
    outTree->Branch("lep2",       "TLorentzVector",  &lep2);     // probe lepton 4-vector
    ///// electron specific /////
    outTree->Branch("trkIso1",    &trkIso1,    "trkIso1/F");     // track isolation of tag lepton
    outTree->Branch("trkIso2",    &trkIso2,    "trkIso2/F");     // track isolation of probe lepton
    outTree->Branch("emIso1",     &emIso1,     "emIso1/F");      // ECAL isolation of tag lepton
    outTree->Branch("emIso2",     &emIso2,     "emIso2/F");      // ECAL isolation of probe lepton
    outTree->Branch("hadIso1",    &hadIso1,    "hadIso1/F");     // HCAL isolation of tag lepton
    outTree->Branch("hadIso2",    &hadIso2,    "hadIso2/F");     // HCAL isolation of probe lepton
    outTree->Branch("pfChIso1",   &pfChIso1,   "pfChIso1/F");    // PF charged hadron isolation of tag lepton
    outTree->Branch("pfChIso2",   &pfChIso2,   "pfChIso2/F");    // PF charged hadron isolation of probe lepton
    outTree->Branch("pfGamIso1",  &pfGamIso1,  "pfGamIso1/F");   // PF photon isolation of tag lepton
    outTree->Branch("pfGamIso2",  &pfGamIso2,  "pfGamIso2/F");   // PF photon isolation of probe lepton
    outTree->Branch("pfNeuIso1",  &pfNeuIso1,  "pfNeuIso1/F");   // PF neutral hadron isolation of tag lepton
    outTree->Branch("pfNeuIso2",  &pfNeuIso2,  "pfNeuIso2/F");   // PF neutral hadron isolation of probe lepton
    outTree->Branch("pfCombIso1", &pfCombIso1, "pfCombIso1/F");  // PF combine isolation of tag lepton
    outTree->Branch("pfCombIso2", &pfCombIso2, "pfCombIso2/F");  // PF combined isolation of probe lepton    
    outTree->Branch("sigieie1",   &sigieie1,   "sigieie1/F");    // sigma-ieta-ieta of tag
    outTree->Branch("sigieie2",   &sigieie2,   "sigieie2/F");    // sigma-ieta-ieta of probe
    outTree->Branch("hovere1",    &hovere1,    "hovere1/F");     // H/E of tag
    outTree->Branch("hovere2",    &hovere2,    "hovere2/F");     // H/E of probe
    outTree->Branch("eoverp1",    &eoverp1,    "eoverp1/F");     // E/p of tag
    outTree->Branch("eoverp2",    &eoverp2,    "eoverp2/F");     // E/p of probe	 
    outTree->Branch("fbrem1",     &fbrem1,     "fbrem1/F");      // brem fraction of tag
    outTree->Branch("fbrem2",     &fbrem2,     "fbrem2/F");      // brem fraction of probe
    outTree->Branch("dphi1",      &dphi1,      "dphi1/F");       // GSF track - ECAL dphi of tag
    outTree->Branch("dphi2",      &dphi2,      "dphi2/F");       // GSF track - ECAL dphi of probe 	
    outTree->Branch("deta1",      &deta1,      "deta1/F");       // GSF track - ECAL deta of tag
    outTree->Branch("deta2",      &deta2,      "deta2/F");       // GSF track - ECAL deta of probe
    outTree->Branch("ecalE1",     &ecalE1,     "ecalE1/F");      // ECAL energy of tag
    outTree->Branch("ecalE2",     &ecalE2,     "ecalE2/F");      // ECAL energy of probe
    outTree->Branch("d01",        &d01,        "d01/F");	 // transverse impact parameter of tag
    outTree->Branch("d02",        &d02,        "d02/F");	 // transverse impact parameter of probe	  
    outTree->Branch("dz1",        &dz1,        "dz1/F");	 // longitudinal impact parameter of tag
    outTree->Branch("dz2",        &dz2,        "dz2/F");	 // longitudinal impact parameter of probe
    outTree->Branch("isConv1",    &isConv1,    "isConv1/i");     // conversion filter flag of tag lepton
    outTree->Branch("isConv2",    &isConv2,    "isConv2/i");     // conversion filter flag of probe lepton
    outTree->Branch("nexphits1",  &nexphits1,  "nexphits1/i");   // number of missing expected inner hits of tag lepton
    outTree->Branch("nexphits2",  &nexphits2,  "nexphits2/i");   // number of missing expected inner hits of probe lepton
    outTree->Branch("typeBits1",  &typeBits1,  "typeBits1/i");   // electron type of tag lepton
    outTree->Branch("typeBits2",  &typeBits2,  "typeBits2/i");   // electron type of probe lepton
    outTree->Branch("sc1",       "TLorentzVector",  &sc1);       // tag supercluster 4-vector
    outTree->Branch("sc2",       "TLorentzVector",  &sc2);       // probe supercluster 4-vector

    //
    // loop through files
    //
    const UInt_t nfiles = samp->fnamev.size();
    for(UInt_t ifile=0; ifile<nfiles; ifile++) {  

      // Read input file and get the TTrees
      cout << "Processing " << samp->fnamev[ifile] << " [xsec = " << samp->xsecv[ifile] << " pb] ... " << endl; cout.flush();
      infile = TFile::Open(samp->fnamev[ifile]); 
      assert(infile);

      Bool_t hasJSON = kFALSE;
      baconhep::RunLumiRangeMap rlrm;
      if(samp->jsonv[ifile].CompareTo("NONE")!=0) { 
	hasJSON = kTRUE;
	rlrm.addJSONFile(samp->jsonv[ifile].Data()); 
      }
  
      eventTree = (TTree*)infile->Get("Events");
      assert(eventTree);  
      eventTree->SetBranchAddress("Info",     &info);        TBranch *infoBr     = eventTree->GetBranch("Info");
      eventTree->SetBranchAddress("Electron", &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");
      eventTree->SetBranchAddress("Photon",   &scArr);       TBranch *scBr       = eventTree->GetBranch("Photon");
      eventTree->SetBranchAddress("PV",   &vertexArr);       TBranch *vertexBr = eventTree->GetBranch("PV");
      Bool_t hasGen = eventTree->GetBranchStatus("GenEvtInfo");
      TBranch *genBr=0, *genPartBr=0;
      if(hasGen) {
        eventTree->SetBranchAddress("GenEvtInfo", &gen); genBr = eventTree->GetBranch("GenEvtInfo");
	eventTree->SetBranchAddress("GenParticle",&genPartArr); genPartBr = eventTree->GetBranch("GenParticle");
      }

      // Compute MC event weight per 1/fb
      const Double_t xsec = samp->xsecv[ifile];
      Double_t totalWeight=0;
      Double_t totalWeightUp=0;
      Double_t totalWeightDown=0;
      Double_t puWeight=0;
      Double_t puWeightUp=0;
      Double_t puWeightDown=0;

      if (hasGen) {
	for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
	  infoBr->GetEntry(ientry);
	  genBr->GetEntry(ientry);
	  puWeight = h_rw->GetBinContent(h_rw->FindBin(info->nPUmean));
	  puWeightUp = h_rw_up->GetBinContent(h_rw_up->FindBin(info->nPUmean));
	  puWeightDown = h_rw_down->GetBinContent(h_rw_down->FindBin(info->nPUmean));
	  totalWeight+=gen->weight*puWeight;
	  totalWeightUp+=gen->weight*puWeightUp;
	  totalWeightDown+=gen->weight*puWeightDown;
	}
      }
      else if (not isData){
	for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
	  puWeight = h_rw->GetBinContent(h_rw->FindBin(info->nPUmean));
	  puWeightUp = h_rw_up->GetBinContent(h_rw_up->FindBin(info->nPUmean));
	  puWeightDown = h_rw_down->GetBinContent(h_rw_down->FindBin(info->nPUmean));
	  totalWeight+= 1.0*puWeight;
	  totalWeightUp+= 1.0*puWeightUp;
	  totalWeightDown+= 1.0*puWeightDown;
	}

      }
      
      //
      // loop over events
      //
      Double_t nsel=0, nselvar=0;
      for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
        infoBr->GetEntry(ientry);

        if(ientry%1000000==0) cout << "Processing event " << ientry << ". " << (double)ientry/(double)eventTree->GetEntries()*100 << " percent done with this file." << endl;

        Double_t weight=1;
	Double_t weightUp=1;
	Double_t weightDown=1;
        if(xsec>0 && totalWeight>0) weight = xsec/totalWeight;
	if(xsec>0 && totalWeightUp>0) weightUp = xsec/totalWeightUp;
	if(xsec>0 && totalWeightDown>0) weightDown = xsec/totalWeightDown;
	if(hasGen) {
	  genPartArr->Clear();
	  genBr->GetEntry(ientry);
          genPartBr->GetEntry(ientry);
	  puWeight = h_rw->GetBinContent(h_rw->FindBin(info->nPUmean));
	  puWeightUp = h_rw_up->GetBinContent(h_rw_up->FindBin(info->nPUmean));
	  puWeightDown = h_rw_down->GetBinContent(h_rw_down->FindBin(info->nPUmean));
	  weight*=gen->weight*puWeight;
	  weightUp*=gen->weight*puWeightUp;
	  weightDown*=gen->weight*puWeightDown;
	}
	
	// veto z -> xx decays for signal and z -> ee for bacground samples (needed for inclusive DYToLL sample)
	if (isWrongFlavor && hasGen && fabs(toolbox::flavor(genPartArr, BOSON_ID))==LEPTON_ID) continue;
	else if (isSignal && hasGen && fabs(toolbox::flavor(genPartArr, BOSON_ID))!=LEPTON_ID) continue;

        // check for certified lumi (if applicable)
        baconhep::RunLumiRangeMap::RunLumiPairType rl(info->runNum, info->lumiSec);      
        if(hasJSON && !rlrm.hasRunLumi(rl)) continue;  

        // trigger requirement
	if (!isEleTrigger(triggerMenu, info->triggerBits, isData)) continue;

        // good vertex requirement
        if(!(info->hasGoodPV)) continue;

	electronArr->Clear();
        electronBr->GetEntry(ientry);
	scArr->Clear();
	scBr->GetEntry(ientry);

	TLorentzVector vTag(0,0,0,0);
	TLorentzVector vTagSC(0,0,0,0);
	Double_t tagPt=0;
	Double_t Pt1=0;
	Double_t Pt2=0;
	Int_t itag=-1;
	Int_t tagscID=-1;
		
	for(Int_t i1=0; i1<electronArr->GetEntriesFast(); i1++) {
          const baconhep::TElectron *tag = (baconhep::TElectron*)((*electronArr)[i1]);
	  
	  // check ECAL gap
	  if(fabs(tag->scEta)>=ECAL_GAP_LOW && fabs(tag->scEta)<=ECAL_GAP_HIGH) continue;
	  
          // apply scale and resolution corrections to MC
          Double_t tagscEt_corr = tag->scEt;
          if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0)
            tagscEt_corr = gRandom->Gaus(tag->scEt*getEleScaleCorr(tag->scEta,0),getEleResCorr(tag->scEta,0));
	  
	  if(tagscEt_corr        < PT_CUT)     continue;  // lepton pT cut
	  if(fabs(tag->scEta)    > ETA_CUT)    continue;  // lepton |eta| cut
	  if(!passEleID(tag,info->rhoIso))     continue;  // lepton selection
	
	  double El_Pt=0;
	  if(doScaleCorr) {
	    El_Pt=gRandom->Gaus(tag->pt*getEleScaleCorr(tag->scEta,0),getEleResCorr(tag->scEta,0));
	  }
	  else
	    {
	      El_Pt=tag->pt;
	    }

	  if(El_Pt>Pt1)
	    {
	      Pt2=Pt1;
	      Pt1=El_Pt;
	    }
	  else if(El_Pt>Pt2&&El_Pt<Pt1)
	    {
	      Pt2=El_Pt;
	    }

	  if(!isEleTriggerObj(triggerMenu, tag->hltMatchBits, kFALSE, isData)) continue;
	  
	  if(El_Pt<tagPt) continue;
	  
	  tagPt=El_Pt;
	  itag=i1;
	  tagscID=tag->scID;

	  // apply scale and resolution corrections to MC
          if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0) {
            vTag.SetPtEtaPhiM(El_Pt, tag->eta, tag->phi, ELE_MASS);
            vTagSC.SetPtEtaPhiM(tagscEt_corr, tag->scEta, tag->scPhi, ELE_MASS);
          } else {
  	    vTag.SetPtEtaPhiM(tag->pt, tag->eta, tag->phi, ELE_MASS);
	    vTagSC.SetPtEtaPhiM(tag->scEt, tag->scEta, tag->scPhi, ELE_MASS);
          }

	  trkIso1    = tag->trkIso;
	  emIso1     = tag->ecalIso;
	  hadIso1    = tag->hcalIso;
	  pfChIso1   = tag->chHadIso;
	  pfGamIso1  = tag->gammaIso;	    
	  pfNeuIso1  = tag->neuHadIso;
	  pfCombIso1 = tag->chHadIso + TMath::Max(tag->neuHadIso + tag->gammaIso - (info->rhoIso)*getEffAreaEl(tag->scEta), 0.);
	  sigieie1   = tag->sieie;
	  hovere1    = tag->hovere;
	  eoverp1    = tag->eoverp;
	  fbrem1     = tag->fbrem;
	  dphi1      = tag->dPhiIn;
	  deta1      = tag->dEtaIn;
	  ecalE1     = tag->ecalEnergy;
	  d01        = tag->d0;
	  dz1        = tag->dz;
	  isConv1    = tag->isConv;
	  nexphits1  = tag->nMissingHits;
	  typeBits1  = tag->typeBits;
	  q1         = tag->q;

	}

	if(tagPt<Pt2) continue;

	TLorentzVector vProbe(0,0,0,0); TLorentzVector vProbeSC(0,0,0,0);
	Double_t probePt=0;
	Int_t iprobe=-1;
	Int_t passID=false;
	UInt_t icat=0;
	
	const baconhep::TElectron *eleProbe=0;

	for(Int_t j=0; j<scArr->GetEntriesFast(); j++) {
	  const baconhep::TPhoton *scProbe = (baconhep::TPhoton*)((*scArr)[j]);
	  
	  if(scProbe->scID == tagscID) continue;

	  // check ECAL gap
	  if(fabs(scProbe->eta)>=ECAL_GAP_LOW && fabs(scProbe->eta)<=ECAL_GAP_HIGH) continue;
	  
	  // apply scale and resolution corrections to MC
	  Double_t scProbept_corr = scProbe->pt;
	  if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0)
	    scProbept_corr = gRandom->Gaus(scProbe->pt*getEleScaleCorr(scProbe->eta,0),getEleResCorr(scProbe->eta,0));
	  
	  if(scProbept_corr        < PT_CUT)  continue;  // Supercluster ET cut ("pt" = corrected by PV position)
	  if(fabs(scProbe->eta)  > ETA_CUT) continue;  // Supercluster |eta| cuts

	  for(Int_t i2=0; i2<electronArr->GetEntriesFast(); i2++) {
	    if(itag==i2) continue;
	    const baconhep::TElectron *ele = (baconhep::TElectron*)((*electronArr)[i2]);
	    if(!(ele->typeBits & baconhep::EEleType::kEcalDriven)) continue;
	    if(scProbe->scID==ele->scID) { 
	      eleProbe = ele; 
	      iprobe   = i2;
	      break; 
	    }
	  }

	  double El_Pt=0;
	  if(doScaleCorr&&eleProbe) {
	    El_Pt=gRandom->Gaus(eleProbe->pt*getEleScaleCorr(scProbe->eta,0),getEleResCorr(scProbe->eta,0));
	  }
	  else if(!doScaleCorr&&eleProbe)
	    {
	      El_Pt=eleProbe->pt;
	    }
	  else
	    {
	      El_Pt=scProbept_corr;
	    }

	  if(passID&&eleProbe&&passEleID(eleProbe,info->rhoIso)&&El_Pt<probePt) continue;
	  if(passID&&eleProbe&&!passEleID(eleProbe,info->rhoIso)) continue;
	  if(passID&&!eleProbe) continue;
	  if(!passID&&eleProbe&&!passEleID(eleProbe,info->rhoIso)&&El_Pt<probePt) continue;
	  if(!passID&&!eleProbe&&El_Pt<probePt) continue;
	  if(!passID&&eleProbe&&passEleID(eleProbe,info->rhoIso)) passID=true;

	  probePt=El_Pt;

	  // apply scale and resolution corrections to MC
	  if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0) {
	    vProbe.SetPtEtaPhiM((eleProbe) ? gRandom->Gaus(eleProbe->pt*getEleScaleCorr(scProbe->eta,0),getEleResCorr(scProbe->eta,0)) : scProbept_corr,
				(eleProbe) ? eleProbe->eta : scProbe->eta,
				(eleProbe) ? eleProbe->phi : scProbe->phi,
				ELE_MASS);
	    vProbeSC.SetPtEtaPhiM((eleProbe) ? gRandom->Gaus(eleProbe->scEt*getEleScaleCorr(scProbe->eta,0),getEleResCorr(scProbe->eta,0)) : gRandom->Gaus(scProbe->pt*getEleScaleCorr(scProbe->eta,0),getEleResCorr(scProbe->eta,0)),
				  scProbe->eta, scProbe->phi, ELE_MASS);
	  } else {
	    vProbe.SetPtEtaPhiM((eleProbe) ? eleProbe->pt : scProbe->pt,
				(eleProbe) ? eleProbe->eta : scProbe->eta,
				(eleProbe) ? eleProbe->phi : scProbe->phi,
				ELE_MASS);
	    vProbeSC.SetPtEtaPhiM((eleProbe) ? eleProbe->scEt : scProbe->pt,
				  scProbe->eta, scProbe->phi, ELE_MASS);
	  }

	  trkIso2    = (eleProbe) ? eleProbe->trkIso        : -1;
	  emIso2     = (eleProbe) ? eleProbe->ecalIso       : -1;
	  hadIso2    = (eleProbe) ? eleProbe->hcalIso       : -1;
	  pfChIso2   = (eleProbe) ? eleProbe->chHadIso      : -1;
	  pfGamIso2  = (eleProbe) ? eleProbe->gammaIso      : -1;
	  pfNeuIso2  = (eleProbe) ? eleProbe->neuHadIso     : -1;	    
	  pfCombIso2 = (eleProbe) ? 
	    eleProbe->chHadIso + TMath::Max(eleProbe->neuHadIso + eleProbe->gammaIso - 
					    (info->rhoIso)*getEffAreaEl(eleProbe->scEta), 0.) :  -1;
	  sigieie2   = (eleProbe) ? eleProbe->sieie         : scProbe->sieie;
	  hovere2    = (eleProbe) ? eleProbe->hovere        : scProbe->hovere;
	  eoverp2    = (eleProbe) ? eleProbe->eoverp        : -1;
	  fbrem2     = (eleProbe) ? eleProbe->fbrem         : -1;
	  dphi2      = (eleProbe) ? eleProbe->dPhiIn        : -999;
	  deta2      = (eleProbe) ? eleProbe->dEtaIn        : -999;
	  ecalE2     = (eleProbe) ? eleProbe->ecalEnergy    : -999;
	  d02        = (eleProbe) ? eleProbe->d0            : -999;
	  dz2        = (eleProbe) ? eleProbe->dz            : -999;
	  isConv2    = (eleProbe) ? eleProbe->isConv        : 0;
	  nexphits2  = (eleProbe) ? eleProbe->nMissingHits  : 0;
	  typeBits2  = (eleProbe) ? eleProbe->typeBits      : 0;
	  q2         = (eleProbe) ? eleProbe->q : -q1;

	  // determine event category
	  if(eleProbe) {
	    if(passEleID(eleProbe,info->rhoIso)) {
	      
	      if(isEleTriggerObj(triggerMenu, eleProbe->hltMatchBits, kFALSE, isData)) {
		icat=eEleEle2HLT;  
	      } 
	      else if(isEleTriggerObj(triggerMenu, eleProbe->hltMatchBits, kTRUE, isData)) {
		icat=eEleEle1HLT1L1; 
	      }
	      else { icat=eEleEle1HLT; }
	    }
	    else { icat=eEleEleNoSel; } 
	  } 
	  else { icat=eEleSC; }
	  
	}

	if(q1 == q2)         continue;  // opposite charge requirement

	// mass window
	TLorentzVector vDilep = vTag + vProbe;
	if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;

	if(icat==0) continue;

	//******** We have a Z candidate! HURRAY! ********
	nsel+=weight;
	nselvar+=weight*weight;

	// Perform matching of dileptons to GEN leptons from Z decay

	Int_t glepq1=-99;
	Int_t glepq2=-99;
	TLorentzVector *gvec=new TLorentzVector(0,0,0,0);
	TLorentzVector *glep1=new TLorentzVector(0,0,0,0);
	TLorentzVector *glep2=new TLorentzVector(0,0,0,0);
	TLorentzVector *gph=new TLorentzVector(0,0,0,0);
	Bool_t hasGenMatch = kFALSE;
	if(isSignal && hasGen) {
	  toolbox::fillGen(genPartArr, BOSON_ID, gvec, glep1, glep2,&glepq1,&glepq2,1);
	  
	  Bool_t match1 = ( ((glep1) && toolbox::deltaR(vTag.Eta(), vTag.Phi(), glep1->Eta(), glep1->Phi())<0.3) || 
			    ((glep2) && toolbox::deltaR(vTag.Eta(), vTag.Phi(), glep2->Eta(), glep2->Phi())<0.3) );
	  
	  Bool_t match2 = ( ((glep1) && toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), glep1->Eta(), glep1->Phi())<0.3) || 
			    ((glep2) && toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), glep2->Eta(), glep2->Phi())<0.3) );
	  
	  if(match1 && match2) {
	    hasGenMatch = kTRUE;
	    if (gvec!=0) {
	      genV=new TLorentzVector(0,0,0,0);
	      genV->SetPtEtaPhiM(gvec->Pt(), gvec->Eta(), gvec->Phi(), gvec->M());
	      genVPt   = gvec->Pt();
	      genVPhi  = gvec->Phi();
	      genVy    = gvec->Rapidity();
	      genVMass = gvec->M();
	    }
	    else {
	      TLorentzVector tvec=*glep1+*glep2;
	      genV=new TLorentzVector(0,0,0,0);
	      genV->SetPtEtaPhiM(tvec.Pt(), tvec.Eta(), tvec.Phi(), tvec.M());
	      genVPt   = tvec.Pt();
	      genVPhi  = tvec.Phi();
	      genVy    = tvec.Rapidity();
	      genVMass = tvec.M();
	    }
	    delete gvec;
	    delete glep1;
	    delete glep2;
	    glep1=0; glep2=0; gvec=0;
	  }
	  else {
	    genV     = new TLorentzVector(0,0,0,0);
	    genVPt   = -999;
	    genVPhi  = -999;
	    genVy    = -999;
	    genVMass = -999;
	  }
	}
	
	if (hasGen) {
	  id_1      = gen->id_1;
	  id_2      = gen->id_2;
	  x_1       = gen->x_1;
	  x_2       = gen->x_2;
	  xPDF_1    = gen->xPDF_1;
	  xPDF_2    = gen->xPDF_2;
	  scalePDF  = gen->scalePDF;
	  weightPDF = gen->weight;
	}
	else {
	  id_1      = -999;
	  id_2      = -999;
	  x_1       = -999;
	  x_2       = -999;
	  xPDF_1    = -999;
	  xPDF_2    = -999;
	  scalePDF  = -999;
	  weightPDF = -999;
	  }

	//
	// Fill tree
	//
	runNum   = info->runNum;
	lumiSec  = info->lumiSec;
	evtNum   = info->evtNum;

	if (hasGenMatch) matchGen=1;
	else matchGen=0;

	category = icat;

	vertexArr->Clear();
	vertexBr->GetEntry(ientry);

	npv      = vertexArr->GetEntries();
	npu      = info->nPUmean;
        genWeight= hasGen ? gen->weight: 1.;
        PUWeight = puWeight;
	scale1fb = weight;
	scale1fbUp = weightUp;
	scale1fbDown = weightDown;
	met      = info->pfMETC;
	metPhi   = info->pfMETCphi;
	sumEt    = 0;
	tkMet    = info->trkMET;
	tkMetPhi = info->trkMETphi;
	tkSumEt  = 0;
	mvaMet   = info->mvaMET;
	mvaMetPhi = info->mvaMETphi; 
	mvaSumEt = 0;
	TVector2 vZPt((vDilep.Pt())*cos(vDilep.Phi()),(vDilep.Pt())*sin(vDilep.Phi()));

	puppiMet = info->puppET;
        puppiMetPhi = info->puppETphi;
	puppiSumEt = 0;
	lep1     = &vTag;
	lep2     = &vProbe;
	dilep    = &vDilep;
	sc1        = &vTagSC;
	sc2        = &vProbeSC;

	TVector2 vMet((info->pfMETC)*cos(info->pfMETCphi), (info->pfMETC)*sin(info->pfMETCphi));
	TVector2 vU = -1.0*(vMet+vZPt);
	u1 = ((vDilep.Px())*(vU.Px()) + (vDilep.Py())*(vU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
	u2 = ((vDilep.Px())*(vU.Py()) - (vDilep.Py())*(vU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|
	
	TVector2 vTkMet((info->trkMET)*cos(info->trkMETphi), (info->trkMET)*sin(info->trkMETphi));        
	TVector2 vTkU = -1.0*(vTkMet+vZPt);
	tkU1 = ((vDilep.Px())*(vTkU.Px()) + (vDilep.Py())*(vTkU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
	tkU2 = ((vDilep.Px())*(vTkU.Py()) - (vDilep.Py())*(vTkU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|
	
	TVector2 vMvaMet((info->mvaMET)*cos(info->mvaMETphi), (info->mvaMET)*sin(info->mvaMETphi));
	TVector2 vMvaU = -1.0*(vMvaMet+vZPt);
	mvaU1 = ((vDilep.Px())*(vMvaU.Px()) + (vDilep.Py())*(vMvaU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
	mvaU2 = ((vDilep.Px())*(vMvaU.Py()) - (vDilep.Py())*(vMvaU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|
        
	TVector2 vPuppiMet((info->puppET)*cos(info->puppETphi), (info->puppET)*sin(info->puppETphi));
	TVector2 vPuppiU = -1.0*(vPuppiMet+vZPt);
	puppiU1 = ((vDilep.Px())*(vPuppiU.Px()) + (vDilep.Py())*(vPuppiU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
	puppiU2 = ((vDilep.Px())*(vPuppiU.Py()) - (vDilep.Py())*(vPuppiU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|

	outTree->Fill();
	delete genV;
	genV=0, dilep=0, lep1=0, lep2=0, sc1=0, sc2=0;
      }
      delete infile;
      infile=0, eventTree=0;    
      
      cout << nsel  << " +/- " << sqrt(nselvar);
      if(!isData) cout << " per 1/fb";
      cout << endl;
    }
    outFile->Write();
    outFile->Close(); 
  }
  delete h_rw;
  delete f_rw;
  delete info;
  delete gen;
  delete genPartArr;
  delete electronArr;
  delete scArr;
  delete vertexArr;
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> e e" << endl;
  cout << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  cout << endl;
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("selectZee"); 
}
示例#7
0
void computeAccSelZeeBinned(const TString conf,            // input file
                            const TString outputDir        // output directory
) {
  gBenchmark->Start("computeAccSelZeeBinned");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t MASS_LOW   = 60;
  const Double_t MASS_HIGH  = 120;
  const Double_t PT_CUT     = 25;
  const Double_t ETA_CUT    = 2.5;
  const Double_t ELE_MASS   = 0.000511;
  const Double_t ETA_BARREL = 1.4442;
  const Double_t ETA_ENDCAP = 1.566;
  
  // efficiency files
  const TString dataHLTEffName     = "../Efficiency/May23_EleHLTEff/analysis/eff.root";
  const TString dataHLTEffName_pos = "../Efficiency/May23_EleHLTEff_pos/analysis/eff.root";
  const TString dataHLTEffName_neg = "../Efficiency/May23_EleHLTEff_neg/analysis/eff.root";
  const TString zeeHLTEffName      = "../Efficiency/Zee_EleHLTEff/analysis/eff.root";
  const TString zeeHLTEffName_pos  = "../Efficiency/Zee_EleHLTEff_pos/analysis/eff.root";
  const TString zeeHLTEffName_neg  = "../Efficiency/Zee_EleHLTEff_neg/analysis/eff.root";
  
  const TString dataGsfSelEffName     = "../Efficiency/May23_EleGsfSelEff/analysis/eff.root";
  const TString dataGsfSelEffName_pos = "../Efficiency/May23_EleGsfSelEff_pos/analysis/eff.root";
  const TString dataGsfSelEffName_neg = "../Efficiency/May23_EleGsfSelEff_neg/analysis/eff.root";
  const TString zeeGsfSelEffName      = "../Efficiency/Zee_EleGsfSelEff/analysis/eff.root";
  const TString zeeGsfSelEffName_pos  = "../Efficiency/Zee_EleGsfSelEff_pos/analysis/eff.root";
  const TString zeeGsfSelEffName_neg  = "../Efficiency/Zee_EleGsfSelEff_neg/analysis/eff.root";
    

  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  vector<TString> fnamev;  // file name per input file
  vector<TString> labelv;  // TLegend label per input file
  vector<Int_t>   colorv;  // plot color per input file
  vector<Int_t>   linev;   // plot line style per input file

  //
  // parse .conf file
  //
  ifstream ifs;
  ifs.open(conf.Data());
  assert(ifs.is_open());
  string line;
  while(getline(ifs,line)) {
    if(line[0]=='#') continue;
    
    string fname;
    Int_t color, linesty;
    stringstream ss(line);
    ss >> fname >> color >> linesty;
    string label = line.substr(line.find('@')+1);
    fnamev.push_back(fname);
    labelv.push_back(label);
    colorv.push_back(color);
    linev.push_back(linesty);
  }
  ifs.close();

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  
  TH2D *h=0;
  
  //
  // HLT efficiency
  //
  cout << "Loading trigger efficiencies..." << endl;

  TFile *dataHLTEffFile_pos = new TFile(dataHLTEffName_pos);
  CEffUser2D dataHLTEff_pos;
  dataHLTEff_pos.loadEff((TH2D*)dataHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataHLTEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataHLTEffFile_neg = new TFile(dataHLTEffName_neg);
  CEffUser2D dataHLTEff_neg;
  dataHLTEff_neg.loadEff((TH2D*)dataHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataHLTEffFile_neg->Get("hErrhEtaPt"));
  
  TFile *zeeHLTEffFile_pos = new TFile(zeeHLTEffName_pos);
  CEffUser2D zeeHLTEff_pos;
  zeeHLTEff_pos.loadEff((TH2D*)zeeHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)zeeHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zeeHLTEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *zeeHLTEffFile_neg = new TFile(zeeHLTEffName_neg);
  CEffUser2D zeeHLTEff_neg;
  zeeHLTEff_neg.loadEff((TH2D*)zeeHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)zeeHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zeeHLTEffFile_neg->Get("hErrhEtaPt"));
  
  h =(TH2D*)dataHLTEffFile_pos->Get("hEffEtaPt");
  TH2D *hHLTErr_pos = new TH2D("hHLTErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                 h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hHLTErr_neg = new TH2D("hHLTErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                 h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  
  //
  // Selection efficiency
  //
  cout << "Loading GSF+selection efficiencies..." << endl;
  
  TFile *dataGsfSelEffFile_pos = new TFile(dataGsfSelEffName_pos);
  CEffUser2D dataGsfSelEff_pos;
  dataGsfSelEff_pos.loadEff((TH2D*)dataGsfSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataGsfSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataGsfSelEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataGsfSelEffFile_neg = new TFile(dataGsfSelEffName_neg);
  CEffUser2D dataGsfSelEff_neg;
  dataGsfSelEff_neg.loadEff((TH2D*)dataGsfSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataGsfSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataGsfSelEffFile_neg->Get("hErrhEtaPt"));

  TFile *zeeGsfSelEffFile_pos = new TFile(zeeGsfSelEffName_pos);
  CEffUser2D zeeGsfSelEff_pos;
  zeeGsfSelEff_pos.loadEff((TH2D*)zeeGsfSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)zeeGsfSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zeeGsfSelEffFile_pos->Get("hErrhEtaPt"));

  TFile *zeeGsfSelEffFile_neg = new TFile(zeeGsfSelEffName_neg);
  CEffUser2D zeeGsfSelEff_neg;
  zeeGsfSelEff_neg.loadEff((TH2D*)zeeGsfSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)zeeGsfSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zeeGsfSelEffFile_neg->Get("hErrhEtaPt"));
 
  h =(TH2D*)dataGsfSelEffFile_pos->Get("hEffEtaPt");
  TH2D *hGsfSelErr_pos = new TH2D("hGsfSelErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hGsfSelErr_neg = new TH2D("hGsfSelErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                                                       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  
  // Data structures to store info from TTrees
  mithep::TEventInfo *info  = new mithep::TEventInfo();
  mithep::TGenInfo   *gen   = new mithep::TGenInfo();
  TClonesArray *electronArr = new TClonesArray("mithep::TElectron");
  
  TFile *infile=0;
  TTree *eventTree=0;
  
  // Variables to store acceptances and uncertainties (per input file)
  vector<Double_t> nEvtsv, nSelv, nSelCorrv;
  vector<Double_t> statErr2v, effErr2v;
  vector<Double_t> accv, accCorrv;
  vector<Double_t> accErrv, accCorrErrv;
    
  //
  // loop through files
  //
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {  

    // Read input file and get the TTrees
    cout << "Processing " << fnamev[ifile] << " ..." << endl;
    infile = new TFile(fnamev[ifile]); 
    assert(infile);
  
    eventTree = (TTree*)infile->Get("Events"); assert(eventTree);  
    eventTree->SetBranchAddress("Info",     &info);        TBranch *infoBr     = eventTree->GetBranch("Info");
    eventTree->SetBranchAddress("Gen",      &gen);         TBranch *genBr      = eventTree->GetBranch("Gen");
    eventTree->SetBranchAddress("Electron", &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");

    nEvtsv.push_back(0);
    nSelv.push_back(0);
    nSelCorrv.push_back(0);
    statErr2v.push_back(0);
    
    //
    // loop over events
    //
    for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
      genBr->GetEntry(ientry);
      if(gen->vmass<MASS_LOW || gen->vmass>MASS_HIGH) continue;

      infoBr->GetEntry(ientry);     
    
      Double_t weight=1;
      nEvtsv[ifile]+=weight;
    
      // trigger requirement               
      ULong_t trigger = kHLT_Ele22_CaloIdL_CaloIsoVL;
      ULong_t trigObj = kHLT_Ele22_CaloIdL_CaloIsoVL_EleObj;   
      if(!(info->triggerBits & trigger)) continue;
      
      // good vertex requirement
      if(!(info->hasGoodPV)) continue;
    
      electronArr->Clear();
      electronBr->GetEntry(ientry);
      for(Int_t i1=0; i1<electronArr->GetEntriesFast(); i1++) {
  	const mithep::TElectron *ele1 = (mithep::TElectron*)((*electronArr)[i1]);
	
	// check ECAL gap
	if(fabs(ele1->scEta)>=ETA_BARREL && fabs(ele1->scEta)<=ETA_ENDCAP) continue;
        
	if(ele1->scEt	     < PT_CUT)	     continue;  // lepton pT cut
        if(fabs(ele1->scEta) > ETA_CUT)	     continue;  // lepton |eta| cut
        if(!passEleID(ele1,info->rhoLowEta)) continue;  // lepton selection

        LorentzVector vEle1(ele1->pt, ele1->eta, ele1->phi, ELE_MASS);
	Bool_t isB1 = (fabs(ele1->scEta)<ETA_BARREL) ? kTRUE : kFALSE;

        for(Int_t i2=i1+1; i2<electronArr->GetEntriesFast(); i2++) {          
	  const mithep::TElectron *ele2 = (mithep::TElectron*)((*electronArr)[i2]);
	  
	  // check ECAL gap
	  if(fabs(ele2->scEta)>=ETA_BARREL && fabs(ele2->scEta)<=ETA_ENDCAP) continue;
        
          if(ele2->scEt        < PT_CUT)       continue;  // lepton pT cut
          if(fabs(ele2->scEta) > ETA_CUT)      continue;  // lepton |eta| cut
	  if(!passEleID(ele2,info->rhoLowEta)) continue;  // lepton selection

          LorentzVector vEle2(ele2->pt, ele2->eta, ele2->phi, ELE_MASS);  
          Bool_t isB2 = (fabs(ele2->scEta)<ETA_BARREL) ? kTRUE : kFALSE;

          // trigger match
	  if(!(ele1->hltMatchBits & trigObj) && !(ele2->hltMatchBits & trigObj)) continue;
	  
	  // mass window
          LorentzVector vDilep = vEle1 + vEle2;
          if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;
          
          
          /******** We have a Z candidate! HURRAY! ********/
       
          Double_t effdata, effmc;
          Double_t sceta1 = (fabs(ele1->scEta)<2.5) ? ele1->scEta : 0.99*(ele1->scEta);
          Double_t sceta2 = (fabs(ele2->scEta)<2.5) ? ele2->scEta : 0.99*(ele2->scEta);
    
          Double_t corr=1;
	  
	  effdata=1; effmc=1;
          if(ele1->q>0) { 
            effdata *= (1.-dataHLTEff_pos.getEff(sceta1, ele1->scEt)); 
            effmc   *= (1.-zeeHLTEff_pos.getEff(sceta1, ele1->scEt)); 
          } else {
            effdata *= (1.-dataHLTEff_neg.getEff(sceta1, ele1->scEt)); 
            effmc   *= (1.-zeeHLTEff_neg.getEff(sceta1, ele1->scEt)); 
          }
          if(ele2->q>0) {
            effdata *= (1.-dataHLTEff_pos.getEff(sceta2, ele2->scEt)); 
            effmc   *= (1.-zeeHLTEff_pos.getEff(sceta2, ele2->scEt));
          } else {
            effdata *= (1.-dataHLTEff_neg.getEff(sceta2, ele2->scEt)); 
            effmc   *= (1.-zeeHLTEff_neg.getEff(sceta2, ele2->scEt));
          }
          effdata = 1.-effdata;
          effmc   = 1.-effmc;
          corr *= effdata/effmc;
    
          effdata=1; effmc=1;
          if(ele1->q>0) { 
            effdata *= dataGsfSelEff_pos.getEff(fabs(sceta1), ele1->scEt); 
            effmc   *= zeeGsfSelEff_pos.getEff(fabs(sceta1), ele1->scEt); 
          } else {
            effdata *= dataGsfSelEff_neg.getEff(fabs(sceta1), ele1->scEt); 
            effmc   *= zeeGsfSelEff_neg.getEff(fabs(sceta1), ele1->scEt); 
          }
          if(ele2->q>0) {
            effdata *= dataGsfSelEff_pos.getEff(fabs(sceta2), ele2->scEt); 
            effmc   *= zeeGsfSelEff_pos.getEff(fabs(sceta2), ele2->scEt);
          } else {
            effdata *= dataGsfSelEff_neg.getEff(fabs(sceta2), ele2->scEt); 
            effmc   *= zeeGsfSelEff_neg.getEff(fabs(sceta2), ele2->scEt);
          }
          corr *= effdata/effmc;
	  
	  nSelv[ifile]+=weight;
	  nSelCorrv[ifile]+=weight*corr;
	  statErr2v[ifile]+=weight*weight*corr*corr;
	  
	  // scale factor uncertainties
//	  Double_t dataerr=0, mcerr=0;
//	  if(ele1->q>0) {	    
//	    effdata = dataGsfSelEff_pos.getEff(fabs(sceta1), ele1->scEt);
//	    errdata = TMath::Max(dataGsfSelEff_pos.getErrl(fabs(sceta1), ele1->scEt), dataGsfSelEff_pos.getErrh(fabs(sceta1), ele1->scEt));
//            effmc   = zeeGsfSelEff_pos.getEff(fabs(sceta1), ele1->scEt); 
//	    errmc   = TMath::Max(zeeGsfSelEff_pos.getErrl(fabs(sceta1), ele1->scEt), zeeGsfSelEff_pos.getErrh(fabs(sceta1), ele1->scEt));
//	    Double_t errGsfSel = weight*corr*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
//	    hGsfSelErr_pos->Fill(fabs(sceta1), ele1->scEt, errGsfSel);
//
//	  } else {
//	    effdata = dataGsfSelEff_neg.getEff(fabs(sceta1), ele1->scEt);
//	    errdata = TMath::Max(dataGsfSelEff_neg.getErrl(fabs(sceta1), ele1->scEt), dataGsfSelEff_neg.getErrh(fabs(sceta1), ele1->scEt));
//            effmc   = zeeGsfSelEff_neg.getEff(fabs(sceta1), ele1->scEt); 
//	    errmc   = TMath::Max(zeeGsfSelEff_neg.getErrl(fabs(sceta1), ele1->scEt), zeeGsfSelEff_neg.getErrh(fabs(sceta1), ele1->scEt));
//	    Double_t errGsfSel = weight*corr*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
//	    hGsfSelErr_neg->Fill(fabs(sceta1), ele1->scEt, errGsfSel);
//	  }
//	  if(ele2->q>0) {	    
//	    effdata = dataGsfSelEff_pos.getEff(fabs(sceta2), ele2->scEt);
//	    errdata = TMath::Max(dataGsfSelEff_pos.getErrl(fabs(sceta2), ele2->scEt), dataGsfSelEff_pos.getErrh(fabs(sceta2), ele2->scEt));
//            effmc   = zeeGsfSelEff_pos.getEff(fabs(sceta2), ele2->scEt); 
//	    errmc   = TMath::Max(zeeGsfSelEff_pos.getErrl(fabs(sceta2), ele2->scEt), zeeGsfSelEff_pos.getErrh(fabs(sceta2), ele2->scEt));
//	    Double_t errGsfSel = weight*corr*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
//	    hGsfSelErr_pos->Fill(fabs(sceta2), ele2->scEt, errGsfSel);
//
//	  } else {
//	    effdata = dataGsfSelEff_neg.getEff(fabs(sceta2), ele2->scEt);
//	    errdata = TMath::Max(dataGsfSelEff_neg.getErrl(fabs(sceta2), ele2->scEt), dataGsfSelEff_neg.getErrh(fabs(sceta2), ele2->scEt));
//            effmc   = zeeGsfSelEff_neg.getEff(fabs(sceta2), ele2->scEt); 
//	    errmc   = TMath::Max(zeeGsfSelEff_neg.getErrl(fabs(sceta2), ele2->scEt), zeeGsfSelEff_neg.getErrh(fabs(sceta2), ele2->scEt));
//	    Double_t errGsfSel = weight*corr*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
//	    hGsfSelErr_neg->Fill(fabs(sceta2), ele2->scEt, errGsfSel);
//	  }
        }
      }      
    }
    
//    Double_t err2=0;
//    for(Int_t iy=0; iy<=hGsfSelErr_pos->GetNbinsY(); iy++) {
//      for(Int_t ix=0; ix<=hGsfSelErr_pos->GetNbinsX(); ix++) {
//	err=hSelErr_pos->GetBinContent(ix,iy);
//	err2+=err*err;
//      }
//    }
//    for(Int_t iy=0; iy<=hGsfSelErr_neg->GetNbinsY(); iy++) {
//      for(Int_t ix=0; ix<=hGsfSelErr_neg->GetNbinsX(); ix++) {
//	err=hSelErr_neg->GetBinContent(ix,iy);
//	err2+=err*err;
//      }
//    }
//    effErr2v.push_back(err2);
    
    // compute acceptances
    accv.push_back(nSelv[ifile]/nEvtsv[ifile]);         accErrv.push_back(sqrt(accv[ifile]*(1.-accv[ifile])/nEvtsv[ifile]));
    accCorrv.push_back(nSelCorrv[ifile]/nEvtsv[ifile]); accCorrErrv.push_back(sqrt(accCorrv[ifile]*(1.-accCorrv[ifile])/nEvtsv[ifile]));
    
    delete infile;
    infile=0, eventTree=0;  
  }  
  delete info;
  delete gen;
  delete electronArr;
  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> e e" << endl;
  cout << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  cout << endl;
  
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {
    cout << "   ================================================" << endl;
    cout << "    Label: " << labelv[ifile] << endl;
    cout << "     File: " << fnamev[ifile] << endl;
    cout << endl;
    cout << "    *** Acceptance ***" << endl;
    cout << "          nominal: " << setw(12) << nSelv[ifile]   << " / " << nEvtsv[ifile] << " = " << accv[ifile]   << " +/- " << accErrv[ifile] << endl;
    cout << "     SF corrected: " << accCorrv[ifile] << " +/- " << accCorrErrv[ifile] << endl;
    cout << endl;
  }
  
  char txtfname[100];
  sprintf(txtfname,"%s/binned.txt",outputDir.Data());
  ofstream txtfile;
  txtfile.open(txtfname);
  txtfile << "*" << endl;
  txtfile << "* SUMMARY" << endl;
  txtfile << "*--------------------------------------------------" << endl;
  txtfile << " Z -> e e" << endl;
  txtfile << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  txtfile << "  pT > " << PT_CUT << endl;
  txtfile << "  |eta| < " << ETA_CUT << endl;
  txtfile << endl;
  
  for(UInt_t ifile=0; ifile<fnamev.size(); ifile++) {
    txtfile << "   ================================================" << endl;
    txtfile << "    Label: " << labelv[ifile] << endl;
    txtfile << "     File: " << fnamev[ifile] << endl;
    txtfile << endl;
    txtfile << "    *** Acceptance ***" << endl;
    txtfile << "          nominal: " << setw(12) << nSelv[ifile]   << " / " << nEvtsv[ifile] << " = " << accv[ifile]   << " +/- " << accErrv[ifile] << endl;
    txtfile << "     SF corrected: " << accCorrv[ifile] << " +/- " << accCorrErrv[ifile] << endl;
    txtfile << endl;
  }
  txtfile.close();
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("computeAccSelZeeBinned"); 
}
示例#8
0
void selectZee(const TString conf,        // input file
               const TString outputDir,   // output directory
	       const Bool_t  doScaleCorr  // apply energy scale corrections?
) {
  gBenchmark->Start("selectZee");

  //--------------------------------------------------------------------------------------------------------------
  // Settings 
  //============================================================================================================== 

  const Double_t MASS_LOW  = 40;
  const Double_t MASS_HIGH = 200;
  const Double_t PT_CUT    = 20;
  const Double_t ETA_CUT   = 2.5;
  const Double_t ELE_MASS  = 0.000511;
  
  const Double_t ECAL_GAP_LOW  = 1.4442;
  const Double_t ECAL_GAP_HIGH = 1.566;
  
  const Double_t escaleNbins  = 6;
  const Double_t escaleEta[]  = { 0.4,     0.8,     1.2,     1.4442,  2,        2.5 };
  const Double_t escaleCorr[] = { 1.00284, 1.00479, 1.00734, 1.00851, 1.00001,  0.982898 };


  //--------------------------------------------------------------------------------------------------------------
  // Main analysis code 
  //==============================================================================================================  

  enum { eEleEle2HLT=1, eEleEle1HLT, eEleEleNoSel, eEleSC };  // event category enum
  
  vector<TString>  snamev;      // sample name (for output files)  
  vector<CSample*> samplev;     // data/MC samples

  //
  // parse .conf file
  //
  confParse(conf, snamev, samplev);
  const Bool_t hasData = (samplev[0]->fnamev.size()>0);

  // Create output directory
  gSystem->mkdir(outputDir,kTRUE);
  const TString ntupDir = outputDir + TString("/ntuples");
  gSystem->mkdir(ntupDir,kTRUE);
  
  //
  // Declare output ntuple variables
  //
  UInt_t  runNum, lumiSec, evtNum;
  UInt_t  matchGen;
  UInt_t  category;
  UInt_t  npv, npu;
  Float_t genVPt, genVPhi, genVy, genVMass;
  Float_t scale1fb;
  Float_t met, metPhi, sumEt, u1, u2;
  Int_t   q1, q2;
  LorentzVector *dilep=0, *lep1=0, *lep2=0;
  ///// electron specific /////
  Float_t trkIso1, emIso1, hadIso1, trkIso2, emIso2, hadIso2;
  Float_t pfChIso1, pfGamIso1, pfNeuIso1, pfCombIso1, pfChIso2, pfGamIso2, pfNeuIso2, pfCombIso2;
  Float_t sigieie1, hovere1, eoverp1, fbrem1, ecalE1, sigieie2, hovere2, eoverp2, fbrem2, ecalE2;
  Float_t dphi1, deta1, dphi2, deta2;
  Float_t d01, dz1, d02, dz2;
  UInt_t  isConv1, nexphits1, typeBits1, isConv2, nexphits2, typeBits2; 
  LorentzVector *sc1=0, *sc2=0;
  
  // Data structures to store info from TTrees
  mithep::TEventInfo *info  = new mithep::TEventInfo();
  mithep::TGenInfo   *gen   = new mithep::TGenInfo();
  TClonesArray *electronArr = new TClonesArray("mithep::TElectron");
  TClonesArray *scArr       = new TClonesArray("mithep::TPhoton");
  TClonesArray *pvArr       = new TClonesArray("mithep::TVertex");
  
  TFile *infile=0;
  TTree *eventTree=0;
  
  //
  // loop over samples
  //  
  for(UInt_t isam=0; isam<samplev.size(); isam++) {
    
    // Assume data sample is first sample in .conf file
    // If sample is empty (i.e. contains no ntuple files), skip to next sample
    if(isam==0 && !hasData) continue;
    
    // Assume signal sample is given name "zee"
    // If it's the signal sample, toggle flag to store GEN W kinematics
    Bool_t isSignal = (snamev[isam].CompareTo("zee",TString::kIgnoreCase)==0);  
    
    CSample* samp = samplev[isam];
  
    //
    // Set up output ntuple
    //
    TString outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.root");
    if(isam==0 && !doScaleCorr) outfilename = ntupDir + TString("/") + snamev[isam] + TString("_select.raw.root");
    TFile *outFile = new TFile(outfilename,"RECREATE"); 
    TTree *outTree = new TTree("Events","Events");

    outTree->Branch("runNum",   &runNum,   "runNum/i");     // event run number
    outTree->Branch("lumiSec",  &lumiSec,  "lumiSec/i");    // event lumi section
    outTree->Branch("evtNum",   &evtNum,   "evtNum/i");     // event number
    outTree->Branch("matchGen", &matchGen, "matchGen/i");   // event has both leptons matched to MC Z->ll
    outTree->Branch("category", &category, "category/i");   // dilepton category
    outTree->Branch("npv",      &npv,      "npv/i");        // number of primary vertices
    outTree->Branch("npu",      &npu,      "npu/i");        // number of in-time PU events (MC)
    outTree->Branch("genVPt",   &genVPt,   "genVPt/F");     // GEN boson pT (signal MC)
    outTree->Branch("genVPhi",  &genVPhi,  "genVPhi/F");    // GEN boson phi (signal MC)
    outTree->Branch("genVy",    &genVy,    "genVy/F");      // GEN boson rapidity (signal MC)
    outTree->Branch("genVMass", &genVMass, "genVMass/F");   // GEN boson mass (signal MC)
    outTree->Branch("scale1fb", &scale1fb, "scale1fb/F");   // event weight per 1/fb (MC)
    outTree->Branch("met",      &met,      "met/F");        // MET
    outTree->Branch("metPhi",   &metPhi,   "metPhi/F");     // phi(MET)
    outTree->Branch("sumEt",    &sumEt,    "sumEt/F");      // Sum ET
    outTree->Branch("u1",       &u1,       "u1/F");         // parallel component of recoil
    outTree->Branch("u2",       &u2,       "u2/F");         // perpendicular component of recoil
    outTree->Branch("q1",       &q1,       "q1/I");         // charge of tag lepton
    outTree->Branch("q2",       &q2,       "q2/I");         // charge of probe lepton
    outTree->Branch("dilep", "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &dilep);  // dilepton 4-vector
    outTree->Branch("lep1",  "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &lep1);   // tag lepton 4-vector
    outTree->Branch("lep2",  "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &lep2);   // probe lepton 4-vector
    ///// electron specific /////
    outTree->Branch("trkIso1",    &trkIso1,    "trkIso1/F");     // track isolation of tag lepton
    outTree->Branch("trkIso2",    &trkIso2,    "trkIso2/F");     // track isolation of probe lepton
    outTree->Branch("emIso1",     &emIso1,     "emIso1/F");      // ECAL isolation of tag lepton
    outTree->Branch("emIso2",     &emIso2,     "emIso2/F");      // ECAL isolation of probe lepton
    outTree->Branch("hadIso1",    &hadIso1,    "hadIso1/F");     // HCAL isolation of tag lepton
    outTree->Branch("hadIso2",    &hadIso2,    "hadIso2/F");     // HCAL isolation of probe lepton
    outTree->Branch("pfChIso1",   &pfChIso1,   "pfChIso1/F");    // PF charged hadron isolation of tag lepton
    outTree->Branch("pfChIso2",   &pfChIso2,   "pfChIso2/F");    // PF charged hadron isolation of probe lepton
    outTree->Branch("pfGamIso1",  &pfGamIso1,  "pfGamIso1/F");   // PF photon isolation of tag lepton
    outTree->Branch("pfGamIso2",  &pfGamIso2,  "pfGamIso2/F");   // PF photon isolation of probe lepton
    outTree->Branch("pfNeuIso1",  &pfNeuIso1,  "pfNeuIso1/F");   // PF neutral hadron isolation of tag lepton
    outTree->Branch("pfNeuIso2",  &pfNeuIso2,  "pfNeuIso2/F");   // PF neutral hadron isolation of probe lepton
    outTree->Branch("pfCombIso1", &pfCombIso1, "pfCombIso1/F");  // PF combine isolation of tag lepton
    outTree->Branch("pfCombIso2", &pfCombIso2, "pfCombIso2/F");  // PF combined isolation of probe lepton    
    outTree->Branch("sigieie1",   &sigieie1,   "sigieie1/F");    // sigma-ieta-ieta of tag
    outTree->Branch("sigieie2",   &sigieie2,   "sigieie2/F");    // sigma-ieta-ieta of probe
    outTree->Branch("hovere1",    &hovere1,    "hovere1/F");     // H/E of tag
    outTree->Branch("hovere2",    &hovere2,    "hovere2/F");     // H/E of probe
    outTree->Branch("eoverp1",    &eoverp1,    "eoverp1/F");     // E/p of tag
    outTree->Branch("eoverp2",    &eoverp2,    "eoverp2/F");     // E/p of probe	 
    outTree->Branch("fbrem1",     &fbrem1,     "fbrem1/F");      // brem fraction of tag
    outTree->Branch("fbrem2",     &fbrem2,     "fbrem2/F");      // brem fraction of probe
    outTree->Branch("dphi1",      &dphi1,      "dphi1/F");       // GSF track - ECAL dphi of tag
    outTree->Branch("dphi2",      &dphi2,      "dphi2/F");       // GSF track - ECAL dphi of probe 	
    outTree->Branch("deta1",      &deta1,      "deta1/F");       // GSF track - ECAL deta of tag
    outTree->Branch("deta2",      &deta2,      "deta2/F");       // GSF track - ECAL deta of probe
    outTree->Branch("ecalE1",     &ecalE1,     "ecalE1/F");      // ECAL energy of tag
    outTree->Branch("ecalE2",     &ecalE2,     "ecalE2/F");      // ECAL energy of probe
    outTree->Branch("d01",        &d01,        "d01/F");	 // transverse impact parameter of tag
    outTree->Branch("d02",        &d02,        "d02/F");	 // transverse impact parameter of probe	  
    outTree->Branch("dz1",        &dz1,        "dz1/F");	 // longitudinal impact parameter of tag
    outTree->Branch("dz2",        &dz2,        "dz2/F");	 // longitudinal impact parameter of probe
    outTree->Branch("isConv1",    &isConv1,    "isConv1/i");     // conversion filter flag of tag lepton
    outTree->Branch("isConv2",    &isConv2,    "isConv2/i");     // conversion filter flag of probe lepton
    outTree->Branch("nexphits1",  &nexphits1,  "nexphits1/i");   // number of missing expected inner hits of tag lepton
    outTree->Branch("nexphits2",  &nexphits2,  "nexphits2/i");   // number of missing expected inner hits of probe lepton
    outTree->Branch("typeBits1",  &typeBits1,  "typeBits1/i");   // electron type of tag lepton
    outTree->Branch("typeBits2",  &typeBits2,  "typeBits2/i");   // electron type of probe lepton
    outTree->Branch("sc1",  "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &sc1);   // tag Supercluster 4-vector
    outTree->Branch("sc2",  "ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<double> >", &sc2);   // probe Supercluster 4-vector 
    
    //
    // loop through files
    //
    const UInt_t nfiles = samp->fnamev.size();
    for(UInt_t ifile=0; ifile<nfiles; ifile++) {  

      // Read input file and get the TTrees
      cout << "Processing " << samp->fnamev[ifile] << " [xsec = " << samp->xsecv[ifile] << " pb] ... "; cout.flush();
      infile = new TFile(samp->fnamev[ifile]); 
      assert(infile);

      Bool_t hasJSON = kFALSE;
      mithep::RunLumiRangeMap rlrm;
      if(samp->jsonv[ifile].CompareTo("NONE")!=0) { 
        hasJSON = kTRUE;
        rlrm.AddJSONFile(samp->jsonv[ifile].Data()); 
      }
  
      eventTree = (TTree*)infile->Get("Events");
      assert(eventTree);  
      eventTree->SetBranchAddress("Info",     &info);        TBranch *infoBr     = eventTree->GetBranch("Info");
      eventTree->SetBranchAddress("Electron", &electronArr); TBranch *electronBr = eventTree->GetBranch("Electron");
      eventTree->SetBranchAddress("Photon",   &scArr);       TBranch *scBr       = eventTree->GetBranch("Photon");
      eventTree->SetBranchAddress("PV",       &pvArr);       TBranch *pvBr       = eventTree->GetBranch("PV");
      Bool_t hasGen = eventTree->GetBranchStatus("Gen");
      TBranch *genBr=0;
      if(hasGen) {
        eventTree->SetBranchAddress("Gen", &gen);
	genBr = eventTree->GetBranch("Gen");
      }
      
      // Compute MC event weight per 1/fb
      Double_t weight = 1;
      const Double_t xsec = samp->xsecv[ifile];
      if(xsec>0) weight = 1000.*xsec/(Double_t)eventTree->GetEntries();     

      //
      // loop over events
      //
      Double_t nsel=0, nselvar=0;
      for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
        infoBr->GetEntry(ientry);
	
	if(genBr) genBr->GetEntry(ientry);
     
        // check for certified lumi (if applicable)
        mithep::RunLumiRangeMap::RunLumiPairType rl(info->runNum, info->lumiSec);      
        if(hasJSON && !rlrm.HasRunLumi(rl)) continue;  

        // trigger requirement               
        ULong64_t trigger = kHLT_Ele22_CaloIdL_CaloIsoVL;
	ULong64_t trigObj = kHLT_Ele22_CaloIdL_CaloIsoVL_EleObj;  
        if(!(info->triggerBits & trigger)) continue;      
      
        // good vertex requirement
        if(!(info->hasGoodPV)) continue;
        pvArr->Clear();
        pvBr->GetEntry(ientry);
      
        //
	// SELECTION PROCEDURE:
	//  (1) Find a good electron matched to trigger -> this will be the "tag"
	//  (2) Pair the tag with Supercluster probes which form a tag+probe mass inside 
	//      the Z window and divide candidates into exclusive categories as follows:
	//      (a) if probe SC is part of a good electron matched to trigger     -> EleEle2HLT category
	//      (b) if probe SC is part of a good electron not matched to trigger -> EleEle1HLT category
	//      (c) if probe SC is part of an electron failing selection cuts     -> EleEleNoSel category
	//      (d) if probe SC is not part of an ECAL driven electron            -> EleSC category
	//	
	electronArr->Clear();
        electronBr->GetEntry(ientry);
	scArr->Clear();
	scBr->GetEntry(ientry);
        for(Int_t i1=0; i1<electronArr->GetEntriesFast(); i1++) {
          const mithep::TElectron *tag = (mithep::TElectron*)((*electronArr)[i1]);
	  
	  // check ECAL gap
	  if(fabs(tag->scEta)>=ECAL_GAP_LOW && fabs(tag->scEta)<=ECAL_GAP_HIGH) continue;
	  
	  Double_t escale1=1;
	  if(doScaleCorr && isam==0) {
	    for(UInt_t ieta=0; ieta<escaleNbins; ieta++) {
	      if(fabs(tag->scEta)<escaleEta[ieta]) {
	        escale1 = escaleCorr[ieta];
		break;
	      }
	    }
	  }
	  
	  if(escale1*(tag->scEt) < PT_CUT)    continue;  // lepton pT cut
	  if(fabs(tag->scEta)    > ETA_CUT)   continue;  // lepton |eta| cut
	  if(!passEleID(tag,info->rhoLowEta)) continue;  // lepton selection
	  if(!(tag->hltMatchBits & trigObj))  continue;  // check trigger matching
	  
	  LorentzVector vTag(escale1*(tag->pt), tag->eta, tag->phi, ELE_MASS);
	  LorentzVector vTagSC(escale1*(tag->scEt), tag->scEta, tag->scPhi, ELE_MASS);
	
	  for(Int_t j=0; j<scArr->GetEntriesFast(); j++) {
	    const mithep::TPhoton *scProbe = (mithep::TPhoton*)((*scArr)[j]);
	    if(scProbe->scID == tag->scID) continue;
	    
	    // check ECAL gap
	    if(fabs(scProbe->scEta)>=ECAL_GAP_LOW && fabs(scProbe->scEta)<=ECAL_GAP_HIGH) continue;
	    
	   Double_t escale2=1;
	    if(doScaleCorr && isam==0) {
	      for(UInt_t ieta=0; ieta<escaleNbins; ieta++) {
	        if(fabs(scProbe->scEta)<escaleEta[ieta]) {
	          escale2 = escaleCorr[ieta];
		  break;
	        }
	      }
	    }
	    
	    if(escale2*(scProbe->pt) < PT_CUT)  continue;  // Supercluster ET cut ("pt" = corrected by PV position)
	    if(fabs(scProbe->scEta)  > ETA_CUT) continue;  // Supercluster |eta| cuts
	    
	    const mithep::TElectron *eleProbe=0;
	    Int_t iprobe=-1;
	    for(Int_t i2=0; i2<electronArr->GetEntriesFast(); i2++) {
	      if(i1==i2) continue;
	      const mithep::TElectron *ele = (mithep::TElectron*)((*electronArr)[i2]);
	      if(!(ele->typeBits & kEcalDriven)) continue;
	      if(scProbe->scID==ele->scID) { 
	        eleProbe = ele; 
		iprobe   = i2;
		break; 
	      }
            }
	    
	    LorentzVector vProbe((eleProbe) ? escale2*(eleProbe->pt) : escale2*(scProbe->pt), 
	                         (eleProbe) ? eleProbe->eta : scProbe->eta, 
				 (eleProbe) ? eleProbe->phi : scProbe->phi, 
				 ELE_MASS);
	    LorentzVector vProbeSC((eleProbe) ? escale2*(eleProbe->scEt) : escale2*(scProbe->pt), 
	                           scProbe->scEta, scProbe->scPhi, ELE_MASS);
	    
	    // mass window
	    LorentzVector vDilep = vTag + vProbe;
	    if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;
	    
	    // determine event category
	    UInt_t icat=0;
	    if(eleProbe) {
	      if(passEleID(eleProbe,info->rhoLowEta)) {
	        if(eleProbe->hltMatchBits & trigObj) {
		  if(i1>iprobe) continue;  // make sure we don't double count EleEle2HLT category
		  icat=eEleEle2HLT;
		
		} else {
		  icat=eEleEle1HLT;
		}	      
	      } else { 
	        icat=eEleEleNoSel;
	      } 
	    } else { 
	      icat=eEleSC;
	    }
	    if(icat==0) continue;
	    
	    
	    /******** We have a Z candidate! HURRAY! ********/
	    
	    nsel+=weight;
            nselvar+=weight*weight;
	    
	    // Perform matching of dileptons to GEN leptons from Z decay
	    Bool_t hasGenMatch = kFALSE;
	    if(isSignal) {
	      Bool_t match1 = ( (abs(gen->id_1)==EGenType::kElectron) && ((toolbox::deltaR(tag->eta, tag->phi, gen->eta_1, gen->phi_1) < 0.5)) )
	                      || ( (abs(gen->id_2)==EGenType::kElectron) && ((toolbox::deltaR(tag->eta, tag->phi, gen->eta_2, gen->phi_2) < 0.5)) );
	      Bool_t match2 = ( (abs(gen->id_1)==EGenType::kElectron) && ((toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), gen->eta_1, gen->phi_1) < 0.5)) )
	                      || ( (abs(gen->id_2)==EGenType::kElectron) && ((toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), gen->eta_2, gen->phi_2) < 0.5)) );
	      if(match1 && match2) hasGenMatch = kTRUE;
	    };
	    
	    //
	    // Fill tree
	    //
	    runNum   = info->runNum;
	    lumiSec  = info->lumiSec;
	    evtNum   = info->evtNum;
	    matchGen = hasGenMatch ? 1 : 0;
	    category = icat;
	    npv      = pvArr->GetEntriesFast();
	    npu      = info->nPU;
	    genVPt   = (hasGen) ? gen->vpt   : 0;
	    genVPhi  = (hasGen) ? gen->vphi  : 0;
	    genVy    = (hasGen) ? gen->vy    : 0;
	    genVMass = (hasGen) ? gen->vmass : 0;
	    scale1fb = weight;
	    met      = info->pfMET;
	    metPhi   = info->pfMETphi;
	    sumEt    = info->pfSumET;
	    
	    lep1 = &vTag;
	    q1   = tag->q;
	    
	    lep2 = &vProbe;
	    q2   = (eleProbe) ? eleProbe->q : -(tag->q);
	    
	    dilep = &vDilep;
	    
	    TVector2 vZPt((vDilep.Pt())*cos(vDilep.Phi()),(vDilep.Pt())*sin(vDilep.Phi()));        
            TVector2 vMet((info->pfMET)*cos(info->pfMETphi), (info->pfMET)*sin(info->pfMETphi));        
            TVector2 vU = -1.0*(vMet+vZPt);
            u1 = ((vDilep.Px())*(vU.Px()) + (vDilep.Py())*(vU.Py()))/(vDilep.Pt());  // u1 = (pT . u)/|pT|
            u2 = ((vDilep.Px())*(vU.Py()) - (vDilep.Py())*(vU.Px()))/(vDilep.Pt());  // u2 = (pT x u)/|pT|
	  
	    ///// electron specific /////
	    sc1        = &vTagSC;
	    trkIso1    = tag->trkIso03;
	    emIso1     = tag->emIso03;
	    hadIso1    = tag->hadIso03;
	    pfChIso1   = tag->pfChIso03;
	    pfGamIso1  = tag->pfGamIso03;	    
	    pfNeuIso1  = tag->pfNeuIso03;
	    pfCombIso1 = tag->pfChIso03 + TMath::Max(tag->pfNeuIso03 + tag->pfGamIso03 - (info->rhoLowEta)*getEffArea(tag->scEta), 0.);
	    sigieie1   = tag->sigiEtaiEta;
	    hovere1    = tag->HoverE;
	    eoverp1    = tag->EoverP;
	    fbrem1     = tag->fBrem;
	    dphi1      = tag->deltaPhiIn;
	    deta1      = tag->deltaEtaIn;
	    ecalE1     = tag->ecalE;
	    d01        = tag->d0;
	    dz1        = tag->dz;
	    isConv1    = tag->isConv;
	    nexphits1  = tag->nExpHitsInner;
	    typeBits1  = tag->typeBits;
	    
	    sc2        = &vProbeSC;
	    trkIso2    = (eleProbe) ? eleProbe->trkIso03      : -1;
	    emIso2     = (eleProbe) ? eleProbe->emIso03       : -1;
	    hadIso2    = (eleProbe) ? eleProbe->hadIso03      : -1;
	    pfChIso2   = (eleProbe) ? eleProbe->pfChIso03     : -1;
	    pfGamIso2  = (eleProbe) ? eleProbe->pfGamIso03    : -1;
	    pfNeuIso2  = (eleProbe) ? eleProbe->pfNeuIso03    : -1;
	    
	    pfCombIso2 = (eleProbe) ? 
	                 eleProbe->pfChIso03 + TMath::Max(eleProbe->pfNeuIso03 + eleProbe->pfGamIso03 - (info->rhoLowEta)*getEffArea(eleProbe->scEta), 0.) : 
			 -1;
	    
	    sigieie2   = (eleProbe) ? eleProbe->sigiEtaiEta   : scProbe->sigiEtaiEta;
	    hovere2    = (eleProbe) ? eleProbe->HoverE        : scProbe->HoverE;
	    eoverp2    = (eleProbe) ? eleProbe->EoverP        : -1;
	    fbrem2     = (eleProbe) ? eleProbe->fBrem         : -1;
	    dphi2      = (eleProbe) ? eleProbe->deltaPhiIn    : -999;
	    deta2      = (eleProbe) ? eleProbe->deltaEtaIn    : -999;
	    ecalE2     = (eleProbe) ? eleProbe->ecalE         : -999;
	    d02        = (eleProbe) ? eleProbe->d0            : -999;
	    dz2        = (eleProbe) ? eleProbe->dz            : -999;
	    isConv2    = (eleProbe) ? eleProbe->isConv        : 0;
	    nexphits2  = (eleProbe) ? eleProbe->nExpHitsInner : 0;
	    typeBits2  = (eleProbe) ? eleProbe->typeBits      : 0; 
	    
	    outTree->Fill();
	  }
        }
      }
      delete infile;
      infile=0, eventTree=0;    

      cout << nsel  << " +/- " << sqrt(nselvar);
      if(isam!=0) cout << " per 1/fb";
      cout << endl;
    }
    outFile->Write();
    outFile->Close(); 
  }
  delete info;
  delete gen;
  delete electronArr;
  delete scArr;
  delete pvArr;
  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> e e" << endl;
  cout << "  Mass window: [" << MASS_LOW << ", " << MASS_HIGH << "]" << endl;
  cout << "  pT > " << PT_CUT << endl;
  cout << "  |eta| < " << ETA_CUT << endl;
  if(doScaleCorr)
    cout << "  *** Scale corrections applied ***" << endl;
  cout << endl;
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("selectZee"); 
}