예제 #1
0
void selectZmmGen(const TString conf="zmmgen.conf", // input file
                  const TString outputDir="."   // output directory
	          ) {
  gBenchmark->Start("selectZmmGen");

//--------------------------------------------------------------------------------------------------------------
  // 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.4;
  const Double_t MUON_MASS = 0.105658369;

  const Int_t BOSON_ID  = 23;
  const Int_t LEPTON_ID = 13;

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

  // load pileup reweighting file  

  TFile *f_rw = TFile::Open("../Tools/pileup_rw_baconDY.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 
  //==============================================================================================================  

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

  //
  // parse .conf file
  //
  confParse(conf, snamev, samplev);
 
  // 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  npv, npu;
  UInt_t  triggerDec;
  UInt_t  goodPV;
  UInt_t  matchTrigger;
  UInt_t  ngenlep;
  TLorentzVector *genlep1=0, *genlep2=0;
  Int_t   genq1, genq2;
  UInt_t nlep;
  TLorentzVector *lep1=0, *lep2=0;
  Int_t   q1, q2;
  Float_t scale1fbGen,scale1fb,scale1fbUp,scale1fbDown;

  std::vector<float> *lheweight = new std::vector<float>();

 // 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 *muonArr    = new TClonesArray("baconhep::TMuon");
  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 signal sample is given name "zmm" - flag to store GEN Z kinematics
    Bool_t isSignal = snamev[isam].Contains("zmm",TString::kIgnoreCase);
    
    // flag to reject Z->mm 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.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("npv",        &npv,        "npv/i");         // number of primary vertices
    outTree->Branch("npu",        &npu,        "npu/i");         // number of in-time PU events (MC)
    outTree->Branch("triggerDec",   &triggerDec,   "triggerDec/i");    // event pass the trigger
    outTree->Branch("goodPV",   &goodPV,   "goodPV/i");    // event has a good PV
    outTree->Branch("matchTrigger",   &matchTrigger,   "matchTrigger/i");    // event has at least one lepton matched to the trigger
    outTree->Branch("ngenlep",     &ngenlep,     "ngenlep/i");      // number of gen leptons
    outTree->Branch("genlep1",   "TLorentzVector",  &genlep1);     // gen lepton1 4-vector
    outTree->Branch("genlep2",   "TLorentzVector",  &genlep2);     // gen lepton2 4-vector
    outTree->Branch("genq1",          &genq1,         "genq1/I");          // charge of lepton1
    outTree->Branch("genq2",          &genq2,         "genq2/I");          // charge of lepton2
    outTree->Branch("nlep",     &nlep,     "nlep/i");      // number of leptons
    outTree->Branch("lep1",       "TLorentzVector",  &lep1);     // lepton1 4-vector
    outTree->Branch("lep2",       "TLorentzVector",  &lep2);     // lepton2 4-vector
    outTree->Branch("q1",          &q1,         "q1/I");          // charge of lepton1
    outTree->Branch("q2",          &q2,         "q2/I");          // charge of lepton2
    outTree->Branch("scale1fbGen",   &scale1fbGen,   "scale1fbGen/F");    // event weight per 1/fb (MC)
    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("lheweight",  &lheweight);

    //
    // 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);
      
      eventTree = (TTree*)infile->Get("Events");
      assert(eventTree);  
      eventTree->SetBranchAddress("Info",     &info);        TBranch *infoBr     = eventTree->GetBranch("Info");
      eventTree->SetBranchAddress("Muon", &muonArr);   TBranch *muonBr = eventTree->GetBranch("Muon");
      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 totalWeightGen=0;
      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));
	  totalWeightGen+=gen->weight;
	  totalWeight+=gen->weight*puWeight;
	  totalWeightUp+=gen->weight*puWeightUp;
	  totalWeightDown+=gen->weight*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 weightGen=1;
	Double_t weight=1;
	Double_t weightUp=1;
	Double_t weightDown=1;
	if(xsec>0 && totalWeightGen>0) weightGen = xsec/totalWeightGen;
        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));
	  weightGen*=gen->weight;
	  weight*=gen->weight*puWeight;
	  weightUp*=gen->weight*puWeightUp;
	  weightDown*=gen->weight*puWeightDown;
	}

	// veto z -> xx decays for signal and z -> mm 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;
     

	// trigger requirement
	Bool_t passMuTrigger = kFALSE;
	if (isMuonTrigger(triggerMenu, info->triggerBits)) passMuTrigger=kTRUE;
	
        // good vertex requirement
	Bool_t hasGoodPV = kFALSE;
        if((info->hasGoodPV)) hasGoodPV=kTRUE;

	muonArr->Clear();
        muonBr->GetEntry(ientry);

	Int_t n_mu=0;
	TLorentzVector vlep1(0., 0., 0., 0.);
	TLorentzVector vlep2(0., 0., 0., 0.);
	Bool_t hasTriggerMatch = kFALSE;

	for(Int_t i1=0; i1<muonArr->GetEntriesFast(); i1++) {
          const baconhep::TMuon *mu = (baconhep::TMuon*)((*muonArr)[i1]);

	  double Mu_Pt=mu->pt;
	
	  if(Mu_Pt     < PT_CUT)        continue;  // lepton pT cut
	  if(fabs(mu->eta) > ETA_CUT)       continue;  // lepton |eta| cut
	  if(!passMuonID(mu))               continue;  // lepton selection

          if(isMuonTriggerObj(triggerMenu, mu->hltMatchBits, kFALSE)) hasTriggerMatch=kTRUE;
	  
	  if(Mu_Pt>vlep1.Pt())
	    {
	      vlep2=vlep1;
	      vlep1.SetPtEtaPhiM(Mu_Pt, mu->eta, mu->phi, MUON_MASS);
	      q2=q1;
	      q1=mu->q;
	    }
	  else if(Mu_Pt<=vlep1.Pt()&&Mu_Pt>vlep2.Pt())
	    {
	      vlep2.SetPtEtaPhiM(Mu_Pt, mu->eta, mu->phi, MUON_MASS);
	      q2=mu->q;
	    }
	 
	  n_mu++;
	}

	Int_t n_genmu=0;
	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(vlep1.Eta(), vlep1.Phi(), glep1->Eta(), glep1->Phi())<0.5) ||
			    ((glep2) && toolbox::deltaR(vlep1.Eta(), vlep1.Phi(), glep2->Eta(), glep2->Phi())<0.5) );
	      
	  Bool_t match2 = ( ((glep1) && toolbox::deltaR(vlep2.Eta(), vlep2.Phi(), glep1->Eta(), glep1->Phi())<0.5) ||
                                ((glep2) && toolbox::deltaR(vlep2.Eta(), vlep2.Phi(), glep2->Eta(), glep2->Phi())<0.5) );

	  for(Int_t i=0; i<genPartArr->GetEntriesFast(); i++) {
	    const baconhep::TGenParticle* genloop = (baconhep::TGenParticle*) ((*genPartArr)[i]);
	    if(fabs(genloop->pdgId)!=22) continue;
	    gph->SetPtEtaPhiM(genloop->pt, genloop->eta, genloop->phi, genloop->mass);
	    if(toolbox::deltaR(gph->Eta(),gph->Phi(),glep1->Eta(),glep1->Phi())<0.1)
	      {
		glep1->operator+=(*gph);
	      }
	    if(toolbox::deltaR(gph->Eta(),gph->Phi(),glep2->Eta(),glep2->Phi())<0.1)
	      {
		glep2->operator+=(*gph);
	      }
	  }

	  if(glep1->Pt() >= PT_CUT && fabs(glep1->Eta())< ETA_CUT)
	    {
	      genlep1=glep1;
	      genq1=glepq1;
	      n_genmu++;
	    }
	  if(glep2->Pt() >= PT_CUT && fabs(glep2->Eta())< ETA_CUT)
	    {
	      genlep2=glep2;
	      genq2=glepq2;
	      n_genmu++;
	    }
	  
	  if(match1 && match2) hasGenMatch = kTRUE;
	}

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

	if (hasGenMatch) matchGen=1;
	else matchGen=0;
	if (passMuTrigger) triggerDec=1;
	else triggerDec=0;
	if (hasGoodPV) goodPV=1;
	else goodPV=0;
	if (hasTriggerMatch) matchTrigger=1;
	else matchTrigger=0;
	
	vertexArr->Clear();
	vertexBr->GetEntry(ientry);
	
	npv      = vertexArr->GetEntries();
	npu      = info->nPUmean;
	scale1fbGen = weightGen;
	scale1fb = weight;
	scale1fbUp = weightUp;
	scale1fbDown = weightDown;

	lheweight->clear();
	for (int j = 0; j<=110; j++)
	  {
	    lheweight->push_back(gen->lheweight[j]);
	  }

	ngenlep=n_genmu;

	nlep=n_mu;
	lep1=&vlep1;
        lep2=&vlep2;
	
	nsel+=weight;
	nselvar+=weight*weight;

	outTree->Fill();

	delete gvec;
	delete glep1;
	delete glep2;
	delete gph;
	lep1=0, lep2=0;
	genlep1=0, genlep2=0;
      }
      delete infile;
      infile=0, eventTree=0;    
      
      cout << nsel  << " +/- " << sqrt(nselvar);
      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 muonArr;
  delete vertexArr;
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
  
  cout << endl;
  cout << "  <> Output saved in " << outputDir << "/" << endl;    
  cout << endl;  
      
  gBenchmark->Show("selectZmmGen"); 
}
예제 #2
0
void computeAccSelZmmBinned(const TString conf,      // input file
                            const TString outputDir  // output directory
                           ) {
    gBenchmark->Start("computeAccSelZmmBinned");

    //--------------------------------------------------------------------------------------------------------------
    // 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.1;
    const Double_t MUON_MASS  = 0.105658369;

    // efficiency files
    const TString dataHLTEffName_pos = "../Efficiency/May23_MuHLTEff_pos/analysis/eff.root";
    const TString dataHLTEffName_neg = "../Efficiency/May23_MuHLTEff_neg/analysis/eff.root";
    const TString zmmHLTEffName_pos  = "../Efficiency/Zmm_MuHLTEff_pos/analysis/eff.root";
    const TString zmmHLTEffName_neg  = "../Efficiency/Zmm_MuHLTEff_neg/analysis/eff.root";

    const TString dataSelEffName_pos = "../Efficiency/May23_MuSelEff_pos/analysis/eff.root";
    const TString dataSelEffName_neg = "../Efficiency/May23_MuSelEff_neg/analysis/eff.root";
    const TString zmmSelEffName_pos  = "../Efficiency/Zmm_MuSelEff_pos/analysis/eff.root";
    const TString zmmSelEffName_neg  = "../Efficiency/Zmm_MuSelEff_neg/analysis/eff.root";

    const TString dataTrkEffName_pos = "../Efficiency/May23_MuTrkEff_pos/analysis/eff.root";
    const TString dataTrkEffName_neg = "../Efficiency/May23_MuTrkEff_neg/analysis/eff.root";
    const TString zmmTrkEffName_pos  = "../Efficiency/Zmm_MuTrkEff_pos/analysis/eff.root";
    const TString zmmTrkEffName_neg  = "../Efficiency/Zmm_MuTrkEff_neg/analysis/eff.root";

    const TString dataStaEffName_pos = "../Efficiency/May23_MuStaEff_iso_pos/analysis/eff.root";
    const TString dataStaEffName_neg = "../Efficiency/May23_MuStaEff_iso_neg/analysis/eff.root";
    const TString zmmStaEffName_pos  = "../Efficiency/Zmm_MuStaEff_iso_pos/analysis/eff.root";
    const TString zmmStaEffName_neg  = "../Efficiency/Zmm_MuStaEff_iso_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);


    //
    // 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 *zmmHLTEffFile_pos = new TFile(zmmHLTEffName_pos);
    CEffUser2D zmmHLTEff_pos;
    zmmHLTEff_pos.loadEff((TH2D*)zmmHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmHLTEffFile_pos->Get("hErrhEtaPt"));

    TFile *zmmHLTEffFile_neg = new TFile(zmmHLTEffName_neg);
    CEffUser2D zmmHLTEff_neg;
    zmmHLTEff_neg.loadEff((TH2D*)zmmHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmHLTEffFile_neg->Get("hErrhEtaPt"));

    //
    // Selection efficiency
    //
    cout << "Loading selection efficiencies..." << endl;

    TFile *dataSelEffFile_pos = new TFile(dataSelEffName_pos);
    CEffUser2D dataSelEff_pos;
    dataSelEff_pos.loadEff((TH2D*)dataSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataSelEffFile_pos->Get("hErrhEtaPt"));

    TFile *dataSelEffFile_neg = new TFile(dataSelEffName_neg);
    CEffUser2D dataSelEff_neg;
    dataSelEff_neg.loadEff((TH2D*)dataSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataSelEffFile_neg->Get("hErrhEtaPt"));

    TFile *zmmSelEffFile_pos = new TFile(zmmSelEffName_pos);
    CEffUser2D zmmSelEff_pos;
    zmmSelEff_pos.loadEff((TH2D*)zmmSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmSelEffFile_pos->Get("hErrhEtaPt"));

    TFile *zmmSelEffFile_neg = new TFile(zmmSelEffName_neg);
    CEffUser2D zmmSelEff_neg;
    zmmSelEff_neg.loadEff((TH2D*)zmmSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmSelEffFile_neg->Get("hErrhEtaPt"));

    //
    // Standalone efficiency
    //
    cout << "Loading standalone efficiencies..." << endl;

    TFile *dataStaEffFile_pos = new TFile(dataStaEffName_pos);
    CEffUser2D dataStaEff_pos;
    dataStaEff_pos.loadEff((TH2D*)dataStaEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataStaEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataStaEffFile_pos->Get("hErrhEtaPt"));

    TFile *dataStaEffFile_neg = new TFile(dataStaEffName_neg);
    CEffUser2D dataStaEff_neg;
    dataStaEff_neg.loadEff((TH2D*)dataStaEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataStaEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataStaEffFile_neg->Get("hErrhEtaPt"));

    TFile *zmmStaEffFile_pos = new TFile(zmmStaEffName_pos);
    CEffUser2D zmmStaEff_pos;
    zmmStaEff_pos.loadEff((TH2D*)zmmStaEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmStaEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmStaEffFile_pos->Get("hErrhEtaPt"));

    TFile *zmmStaEffFile_neg = new TFile(zmmStaEffName_neg);
    CEffUser2D zmmStaEff_neg;
    zmmStaEff_neg.loadEff((TH2D*)zmmStaEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmStaEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmStaEffFile_neg->Get("hErrhEtaPt"));

    //
    // Tracker efficiency
    //
    cout << "Loading track efficiencies..." << endl;

    TFile *dataTrkEffFile_pos = new TFile(dataTrkEffName_pos);
    CEffUser2D dataTrkEff_pos;
    dataTrkEff_pos.loadEff((TH2D*)dataTrkEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataTrkEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataTrkEffFile_pos->Get("hErrhEtaPt"));

    TFile *dataTrkEffFile_neg = new TFile(dataTrkEffName_neg);
    CEffUser2D dataTrkEff_neg;
    dataTrkEff_neg.loadEff((TH2D*)dataTrkEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataTrkEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataTrkEffFile_neg->Get("hErrhEtaPt"));

    TFile *zmmTrkEffFile_pos = new TFile(zmmTrkEffName_pos);
    CEffUser2D zmmTrkEff_pos;
    zmmTrkEff_pos.loadEff((TH2D*)zmmTrkEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmTrkEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmTrkEffFile_pos->Get("hErrhEtaPt"));

    TFile *zmmTrkEffFile_neg = new TFile(zmmTrkEffName_neg);
    CEffUser2D zmmTrkEff_neg;
    zmmTrkEff_neg.loadEff((TH2D*)zmmTrkEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmTrkEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmTrkEffFile_neg->Get("hErrhEtaPt"));


    // Data structures to store info from TTrees
    mithep::TEventInfo *info = new mithep::TEventInfo();
    mithep::TGenInfo   *gen  = new mithep::TGenInfo();
    TClonesArray *muonArr    = new TClonesArray("mithep::TMuon");

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

    // Variables to store acceptances and uncertainties (per input file)
    vector<Double_t> nEvtsv, nSelv, nSelCorrv;
    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("Muon", &muonArr);
        TBranch *muonBr = eventTree->GetBranch("Muon");

        nEvtsv.push_back(0);
        nSelv.push_back(0);
        nSelCorrv.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_Mu15_eta2p1;
            ULong_t trigObj = kHLT_Mu15_eta2p1_MuObj;
            if(!(info->triggerBits & trigger)) continue;

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

            muonArr->Clear();
            muonBr->GetEntry(ientry);
            for(Int_t i1=0; i1<muonArr->GetEntriesFast(); i1++) {
                const mithep::TMuon *mu1 = (mithep::TMuon*)((*muonArr)[i1]);

                if(mu1->pt	  < PT_CUT)  continue;  // lepton pT cut
                if(fabs(mu1->eta) > ETA_CUT) continue;  // lepton |eta| cut
                if(!passMuonID(mu1))	     continue;  // lepton selection

                LorentzVector vMu1(mu1->pt, mu1->eta, mu1->phi, MUON_MASS);

                for(Int_t i2=i1+1; i2<muonArr->GetEntriesFast(); i2++) {
                    const mithep::TMuon *mu2 = (mithep::TMuon*)((*muonArr)[i2]);

                    if(mu1->q == mu2->q)	       continue;  // opposite charge requirement
                    if(mu2->pt        < PT_CUT)  continue;  // lepton pT cut
                    if(fabs(mu2->eta) > ETA_CUT) continue;  // lepton |eta| cut
                    if(!passMuonID(mu2))	       continue;  // lepton selection

                    LorentzVector vMu2(mu2->pt, mu2->eta, mu2->phi, MUON_MASS);

                    // trigger match
                    if(!(mu1->hltMatchBits & trigObj) && !(mu2->hltMatchBits & trigObj)) continue;

                    // mass window
                    LorentzVector vDilep = vMu1 + vMu2;
                    if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;


                    /******** We have a Z candidate! HURRAY! ********/

                    Double_t effdata, effmc;
                    Double_t corr=1;

                    effdata=1;
                    effmc=1;
                    if(mu1->q>0) {
                        effdata *= (1.-dataHLTEff_pos.getEff(mu1->eta, mu1->pt));
                        effmc   *= (1.-zmmHLTEff_pos.getEff(mu1->eta, mu1->pt));
                    } else {
                        effdata *= (1.-dataHLTEff_neg.getEff(mu1->eta, mu1->pt));
                        effmc   *= (1.-zmmHLTEff_neg.getEff(mu1->eta, mu1->pt));
                    }
                    if(mu2->q>0) {
                        effdata *= (1.-dataHLTEff_pos.getEff(mu2->eta, mu2->pt));
                        effmc   *= (1.-zmmHLTEff_pos.getEff(mu2->eta, mu2->pt));
                    } else {
                        effdata *= (1.-dataHLTEff_neg.getEff(mu2->eta, mu2->pt));
                        effmc   *= (1.-zmmHLTEff_neg.getEff(mu2->eta, mu2->pt));
                    }
                    effdata = 1.-effdata;
                    effmc   = 1.-effmc;
                    corr *= effdata/effmc;

                    effdata=1;
                    effmc=1;
                    if(mu1->q>0) {
                        effdata *= dataSelEff_pos.getEff(mu1->eta, mu1->pt);
                        effmc   *= zmmSelEff_pos.getEff(mu1->eta, mu1->pt);
                    } else {
                        effdata *= dataSelEff_neg.getEff(mu1->eta, mu1->pt);
                        effmc   *= zmmSelEff_neg.getEff(mu1->eta, mu1->pt);
                    }
                    if(mu2->q>0) {
                        effdata *= dataSelEff_pos.getEff(mu2->eta, mu2->pt);
                        effmc   *= zmmSelEff_pos.getEff(mu2->eta, mu2->pt);
                    } else {
                        effdata *= dataSelEff_neg.getEff(mu2->eta, mu2->pt);
                        effmc   *= zmmSelEff_neg.getEff(mu2->eta, mu2->pt);
                    }
                    corr *= effdata/effmc;

                    effdata=1;
                    effmc=1;
                    if(mu1->q>0) {
                        effdata *= dataStaEff_pos.getEff(fabs(mu1->eta), mu1->pt);
                        effmc   *= zmmStaEff_pos.getEff(fabs(mu1->eta), mu1->pt);
                    } else {
                        effdata *= dataStaEff_neg.getEff(fabs(mu1->eta), mu1->pt);
                        effmc   *= zmmStaEff_neg.getEff(fabs(mu1->eta), mu1->pt);
                    }
                    if(mu2->q>0) {
                        effdata *= dataStaEff_pos.getEff(fabs(mu2->eta), mu2->pt);
                        effmc   *= zmmStaEff_pos.getEff(fabs(mu2->eta), mu2->pt);
                    } else {
                        effdata *= dataStaEff_neg.getEff(fabs(mu2->eta), mu2->pt);
                        effmc   *= zmmStaEff_neg.getEff(fabs(mu2->eta), mu2->pt);
                    }
                    corr *= effdata/effmc;

                    effdata=1;
                    effmc=1;
                    if(mu1->q>0) {
                        effdata *= dataTrkEff_pos.getEff(fabs(mu1->eta), mu1->pt);
                        effmc   *= zmmTrkEff_pos.getEff(fabs(mu1->eta), mu1->pt);
                    } else {
                        effdata *= dataTrkEff_neg.getEff(fabs(mu1->eta), mu1->pt);
                        effmc   *= zmmTrkEff_neg.getEff(fabs(mu1->eta), mu1->pt);
                    }
                    if(mu2->q>0) {
                        effdata *= dataTrkEff_pos.getEff(fabs(mu2->eta), mu2->pt);
                        effmc   *= zmmTrkEff_pos.getEff(fabs(mu2->eta), mu2->pt);
                    } else {
                        effdata *= dataTrkEff_neg.getEff(fabs(mu2->eta), mu2->pt);
                        effmc   *= zmmTrkEff_neg.getEff(fabs(mu2->eta), mu2->pt);
                    }
                    corr *= effdata/effmc;

                    nSelv[ifile]    +=weight;
                    nSelCorrv[ifile]+=weight*corr;
                }
            }
        }

        // compute acceptances
        accv.push_back(nSelv[ifile]/nEvtsv[ifile]);
        accErrv.push_back(accv[ifile]*sqrt((1.-accv[ifile])/nEvtsv[ifile]));
        accCorrv.push_back(nSelCorrv[ifile]/nEvtsv[ifile]);
        accCorrErrv.push_back(accCorrv[ifile]*sqrt((1.-accCorrv[ifile])/nEvtsv[ifile]));

        delete infile;
        infile=0, eventTree=0;
    }
    delete info;
    delete gen;
    delete muonArr;


    //--------------------------------------------------------------------------------------------------------------
    // Output
    //==============================================================================================================
    cout << "*" << endl;
    cout << "* SUMMARY" << endl;
    cout << "*--------------------------------------------------" << endl;
    cout << " Z -> mu mu" << 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 -> mu mu" << 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("computeAccSelZmmBinned");
}
void computeAccSelZmmBinned_PileupSys(const TString conf,      // input file
			    const TString inputDir,
                            const TString outputDir,  // output directory
			    const Int_t   doPU,
			    const TString PUtype
) {
  gBenchmark->Start("computeAccSelZmmBinned");

  //--------------------------------------------------------------------------------------------------------------
  // 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.4;
  const Double_t MUON_MASS  = 0.105658369;

  const Int_t BOSON_ID  = 23;
  const Int_t LEPTON_ID = 13;
  
  // efficiency files
  const TString dataHLTEffName_pos = inputDir + "MuHLTEff/MGpositive/eff.root";
  const TString dataHLTEffName_neg = inputDir + "MuHLTEff/MGnegative/eff.root";
  const TString zmmHLTEffName_pos  = inputDir + "MuHLTEff/CTpositive/eff.root";
  const TString zmmHLTEffName_neg  = inputDir + "MuHLTEff/CTnegative/eff.root";

  const TString dataSelEffName_pos = inputDir + "MuSITEff/MGpositive_FineBin/eff.root";
  const TString dataSelEffName_neg = inputDir + "MuSITEff/MGnegative_FineBin/eff.root";
  const TString zmmSelEffName_pos  = inputDir + "MuSITEff/CTpositive/eff.root";
  const TString zmmSelEffName_neg  = inputDir + "MuSITEff/CTnegative/eff.root";

  const TString dataTrkEffName_pos = inputDir + "MuSITEff/MGpositive_FineBin/eff.root";
  const TString dataTrkEffName_neg = inputDir + "MuSITEff/MGnegative_FineBin/eff.root";
  const TString zmmTrkEffName_pos  = inputDir + "MuSITEff/CTpositive/eff.root";
  const TString zmmTrkEffName_neg  = inputDir + "MuSITEff/CTnegative/eff.root";

  const TString dataStaEffName_pos = inputDir + "MuStaEff/MGpositive/eff.root";
  const TString dataStaEffName_neg = inputDir + "MuStaEff/MGnegative/eff.root";
  const TString zmmStaEffName_pos  = inputDir + "MuStaEff/CTpositive/eff.root";
  const TString zmmStaEffName_neg  = inputDir + "MuStaEff/CTnegative/eff.root";
  
  // load pileup reweighting file
  TFile *f_rw = TFile::Open("../Tools/puWeights_76x.root", "read");
  TH1D *h_rw = (TH1D*) f_rw->Get(PUtype.Data());
 
  //--------------------------------------------------------------------------------------------------------------
  // 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 *zmmHLTEffFile_pos = new TFile(zmmHLTEffName_pos);
  CEffUser2D zmmHLTEff_pos;
  zmmHLTEff_pos.loadEff((TH2D*)zmmHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmHLTEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *zmmHLTEffFile_neg = new TFile(zmmHLTEffName_neg);
  CEffUser2D zmmHLTEff_neg;
  zmmHLTEff_neg.loadEff((TH2D*)zmmHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmHLTEffFile_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 selection efficiencies..." << endl;
  
  TFile *dataSelEffFile_pos = new TFile(dataSelEffName_pos);
  CEffUser2D dataSelEff_pos;
  dataSelEff_pos.loadEff((TH2D*)dataSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataSelEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataSelEffFile_neg = new TFile(dataSelEffName_neg);
  CEffUser2D dataSelEff_neg;
  dataSelEff_neg.loadEff((TH2D*)dataSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataSelEffFile_neg->Get("hErrhEtaPt"));
  
  TFile *zmmSelEffFile_pos = new TFile(zmmSelEffName_pos);
  CEffUser2D zmmSelEff_pos;
  zmmSelEff_pos.loadEff((TH2D*)zmmSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmSelEffFile_pos->Get("hErrhEtaPt"));

  TFile *zmmSelEffFile_neg = new TFile(zmmSelEffName_neg);
  CEffUser2D zmmSelEff_neg;
  zmmSelEff_neg.loadEff((TH2D*)zmmSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmSelEffFile_neg->Get("hErrhEtaPt"));

  h =(TH2D*)dataSelEffFile_pos->Get("hEffEtaPt");
  TH2D *hSelErr_pos = new TH2D("hSelErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
			       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hSelErr_neg = new TH2D("hSelErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
			       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());

  //
  // Standalone efficiency
  //
  cout << "Loading standalone efficiencies..." << endl;
  
  TFile *dataStaEffFile_pos = new TFile(dataStaEffName_pos);
  CEffUser2D dataStaEff_pos;
  dataStaEff_pos.loadEff((TH2D*)dataStaEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataStaEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataStaEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataStaEffFile_neg = new TFile(dataStaEffName_neg);
  CEffUser2D dataStaEff_neg;
  dataStaEff_neg.loadEff((TH2D*)dataStaEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataStaEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataStaEffFile_neg->Get("hErrhEtaPt"));
  
  TFile *zmmStaEffFile_pos = new TFile(zmmStaEffName_pos);
  CEffUser2D zmmStaEff_pos;
  zmmStaEff_pos.loadEff((TH2D*)zmmStaEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmStaEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmStaEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *zmmStaEffFile_neg = new TFile(zmmStaEffName_neg);
  CEffUser2D zmmStaEff_neg;
  zmmStaEff_neg.loadEff((TH2D*)zmmStaEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmStaEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmStaEffFile_neg->Get("hErrhEtaPt"));

  h =(TH2D*)dataStaEffFile_pos->Get("hEffEtaPt");
  TH2D *hStaErr_pos = new TH2D("hStaErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
			       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hStaErr_neg = new TH2D("hStaErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
			       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());

  //
  // Tracker efficiency
  //
  cout << "Loading track efficiencies..." << endl;
  
  TFile *dataTrkEffFile_pos = new TFile(dataTrkEffName_pos);
  CEffUser2D dataTrkEff_pos;
  dataTrkEff_pos.loadEff((TH2D*)dataTrkEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataTrkEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataTrkEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *dataTrkEffFile_neg = new TFile(dataTrkEffName_neg);
  CEffUser2D dataTrkEff_neg;
  dataTrkEff_neg.loadEff((TH2D*)dataTrkEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataTrkEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataTrkEffFile_neg->Get("hErrhEtaPt"));
  
  TFile *zmmTrkEffFile_pos = new TFile(zmmTrkEffName_pos);
  CEffUser2D zmmTrkEff_pos;
  zmmTrkEff_pos.loadEff((TH2D*)zmmTrkEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmTrkEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmTrkEffFile_pos->Get("hErrhEtaPt"));
  
  TFile *zmmTrkEffFile_neg = new TFile(zmmTrkEffName_neg);
  CEffUser2D zmmTrkEff_neg;
  zmmTrkEff_neg.loadEff((TH2D*)zmmTrkEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmTrkEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmTrkEffFile_neg->Get("hErrhEtaPt"));

  h =(TH2D*)dataTrkEffFile_pos->Get("hEffEtaPt");
  TH2D *hTrkErr_pos = new TH2D("hTrkErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
			       h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hTrkErr_neg = new TH2D("hTrkErr_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 *muonArr        = new TClonesArray("baconhep::TMuon");
  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("Muon",          &muonArr); TBranch *muonBr = eventTree->GetBranch("Muon");   
    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 (!isMuonTrigger(triggerMenu, info->triggerBits)) continue;

      // good vertex requirement
      if(!(info->hasGoodPV)) continue;
    
      muonArr->Clear();
      muonBr->GetEntry(ientry);

      for(Int_t i1=0; i1<muonArr->GetEntriesFast(); i1++) {
  	const baconhep::TMuon *mu1 = (baconhep::TMuon*)((*muonArr)[i1]);

        if(mu1->pt	  < PT_CUT)  continue;  // lepton pT cut
        if(fabs(mu1->eta) > ETA_CUT) continue;  // lepton |eta| cut
        if(!passMuonID(mu1))	     continue;  // lepton selection
	
	TLorentzVector vMu1(0,0,0,0);
	vMu1.SetPtEtaPhiM(mu1->pt, mu1->eta, mu1->phi, MUON_MASS);

        for(Int_t i2=i1+1; i2<muonArr->GetEntriesFast(); i2++) {
          const baconhep::TMuon *mu2 = (baconhep::TMuon*)((*muonArr)[i2]);
        
          if(mu1->q == mu2->q)	       continue;  // opposite charge requirement
          if(mu2->pt        < PT_CUT)  continue;  // lepton pT cut
          if(fabs(mu2->eta) > ETA_CUT) continue;  // lepton |eta| cut
	  if(!passMuonID(mu2))	       continue;  // lepton selection

          TLorentzVector vMu2(0,0,0,0);
	  vMu2.SetPtEtaPhiM(mu2->pt, mu2->eta, mu2->phi, MUON_MASS);  

          // trigger match
	  if(!isMuonTriggerObj(triggerMenu, mu1->hltMatchBits, kFALSE) && !isMuonTriggerObj(triggerMenu, mu2->hltMatchBits, kFALSE)) continue;
	  
	  // mass window
          TLorentzVector vDilep = vMu1 + vMu2;
          if((vDilep.M()<MASS_LOW) || (vDilep.M()>MASS_HIGH)) continue;
          
          /******** We have a Z candidate! HURRAY! ********/
          Double_t effdata, effmc;
	  Double_t corr=1;
	  
	  effdata=1; effmc=1;    
          if(mu1->q>0) { 
            effdata *= (1.-dataHLTEff_pos.getEff(mu1->eta, mu1->pt)); 
            effmc   *= (1.-zmmHLTEff_pos.getEff(mu1->eta, mu1->pt)); 
          } else {
            effdata *= (1.-dataHLTEff_neg.getEff(mu1->eta, mu1->pt)); 
            effmc   *= (1.-zmmHLTEff_neg.getEff(mu1->eta, mu1->pt)); 
          }
          if(mu2->q>0) {
            effdata *= (1.-dataHLTEff_pos.getEff(mu2->eta, mu2->pt)); 
            effmc   *= (1.-zmmHLTEff_pos.getEff(mu2->eta, mu2->pt));
          } else {
            effdata *= (1.-dataHLTEff_neg.getEff(mu2->eta, mu2->pt)); 
            effmc   *= (1.-zmmHLTEff_neg.getEff(mu2->eta, mu2->pt));
          }
          effdata = 1.-effdata;
          effmc   = 1.-effmc;
          corr *= effdata/effmc;
    
          effdata=1; effmc=1;
          if(mu1->q>0) { 
            effdata *= dataSelEff_pos.getEff(mu1->eta, mu1->pt); 
            effmc   *= zmmSelEff_pos.getEff(mu1->eta, mu1->pt); 
          } else {
            effdata *= dataSelEff_neg.getEff(mu1->eta, mu1->pt); 
            effmc   *= zmmSelEff_neg.getEff(mu1->eta, mu1->pt); 
          }
          if(mu2->q>0) {
            effdata *= dataSelEff_pos.getEff(mu2->eta, mu2->pt); 
            effmc   *= zmmSelEff_pos.getEff(mu2->eta, mu2->pt);
          } else {
            effdata *= dataSelEff_neg.getEff(mu2->eta, mu2->pt); 
            effmc   *= zmmSelEff_neg.getEff(mu2->eta, mu2->pt);
          }
          corr *= effdata/effmc;
    
          effdata=1; effmc=1;
          if(mu1->q>0) { 
            effdata *= dataStaEff_pos.getEff(mu1->eta, mu1->pt); 
            effmc   *= zmmStaEff_pos.getEff(mu1->eta, mu1->pt); 
          } else {
            effdata *= dataStaEff_neg.getEff(mu1->eta, mu1->pt); 
            effmc   *= zmmStaEff_neg.getEff(mu1->eta, mu1->pt); 
          }
          if(mu2->q>0) {
            effdata *= dataStaEff_pos.getEff(mu2->eta, mu2->pt); 
            effmc   *= zmmStaEff_pos.getEff(mu2->eta, mu2->pt);
          } else {
            effdata *= dataStaEff_neg.getEff(mu2->eta, mu2->pt); 
            effmc   *= zmmStaEff_neg.getEff(mu2->eta, mu2->pt);
          }
          corr *= effdata/effmc;
    
          effdata=1; effmc=1;
          if(mu1->q>0) { 
            effdata *= dataTrkEff_pos.getEff(mu1->eta, mu1->pt); 
            effmc   *= zmmTrkEff_pos.getEff(mu1->eta, mu1->pt); 
          } else {
            effdata *= dataTrkEff_neg.getEff(mu1->eta, mu1->pt); 
            effmc   *= zmmTrkEff_neg.getEff(mu1->eta, mu1->pt); 
          }
          if(mu2->q>0) {
            effdata *= dataTrkEff_pos.getEff(mu2->eta, mu2->pt); 
            effmc   *= zmmTrkEff_pos.getEff(mu2->eta, mu2->pt);
          } else {
            effdata *= dataTrkEff_neg.getEff(mu2->eta, mu2->pt); 
            effmc   *= zmmTrkEff_neg.getEff(mu2->eta, mu2->pt);
          }
          //corr *= effdata/effmc;
	  
	  // scale factor uncertainties                                                                                                                                         
	  // TRACKER
          if(mu1->q>0) {
            Double_t effdata = dataTrkEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataTrkEff_pos.getErrLow(mu1->eta, mu1->pt), dataTrkEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmTrkEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmTrkEff_pos.getErrLow(mu1->eta, mu1->pt), zmmTrkEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t errTrk = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hTrkErr_pos->Fill(mu1->eta, mu1->pt, errTrk);
          } else {
            Double_t effdata = dataTrkEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataTrkEff_neg.getErrLow(mu1->eta, mu1->pt), dataTrkEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmTrkEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmTrkEff_neg.getErrLow(mu1->eta, mu1->pt), zmmTrkEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t errTrk = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hTrkErr_neg->Fill(mu1->eta, mu1->pt, errTrk);
          }

          if(mu2->q>0) {
            Double_t effdata = dataTrkEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataTrkEff_pos.getErrLow(mu2->eta, mu2->pt), dataTrkEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmTrkEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmTrkEff_pos.getErrLow(mu2->eta, mu2->pt), zmmTrkEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t errTrk = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
	    /*   if(mu2->eta>1.2 && mu2->eta<2.1) 
	      {
		errTrk=0.0013;
		}*/
            hTrkErr_pos->Fill(mu2->eta, mu2->pt, errTrk);
          } else {
            Double_t effdata = dataTrkEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataTrkEff_neg.getErrLow(mu2->eta, mu2->pt), dataTrkEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmTrkEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmTrkEff_neg.getErrLow(mu2->eta, mu2->pt), zmmTrkEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t errTrk = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
	    /*  if(mu2->eta>1.2 && mu2->eta<2.1) 
	      {
		errTrk=0.0013;
	      }
	      hTrkErr_neg->Fill(mu2->eta, mu2->pt, errTrk);*/
          }
	  // STANDALONE
          if(mu1->q>0) {
            Double_t effdata = dataStaEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataStaEff_pos.getErrLow(mu1->eta, mu1->pt), dataStaEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmStaEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmStaEff_pos.getErrLow(mu1->eta, mu1->pt), zmmStaEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t errSta = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hStaErr_pos->Fill(mu1->eta, mu1->pt, errSta);
          } else {
            Double_t effdata = dataStaEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataStaEff_neg.getErrLow(mu1->eta, mu1->pt), dataStaEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmStaEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmStaEff_neg.getErrLow(mu1->eta, mu1->pt), zmmStaEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t errSta = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hStaErr_neg->Fill(mu1->eta, mu1->pt, errSta);
          }

          if(mu2->q>0) {
            Double_t effdata = dataStaEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataStaEff_pos.getErrLow(mu2->eta, mu2->pt), dataStaEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmStaEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmStaEff_pos.getErrLow(mu2->eta, mu2->pt), zmmStaEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t errSta = ((effdata/effmc))*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hStaErr_pos->Fill(mu2->eta, mu2->pt, errSta);
          } else {
            Double_t effdata = dataStaEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataStaEff_neg.getErrLow(mu2->eta, mu2->pt), dataStaEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmStaEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmStaEff_neg.getErrLow(mu2->eta, mu2->pt), zmmStaEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t errSta = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hStaErr_neg->Fill(mu2->eta, mu2->pt, errSta);
	  }

	  // SELECTION
          if(mu1->q>0) {
            Double_t effdata = dataSelEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataSelEff_pos.getErrLow(mu1->eta, mu1->pt), dataSelEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmSelEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmSelEff_pos.getErrLow(mu1->eta, mu1->pt), zmmSelEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t errSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hSelErr_pos->Fill(mu1->eta, mu1->pt, errSel);
          } else {
            Double_t effdata = dataSelEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataSelEff_neg.getErrLow(mu1->eta, mu1->pt), dataSelEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmSelEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmSelEff_neg.getErrLow(mu1->eta, mu1->pt), zmmSelEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t errSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hSelErr_neg->Fill(mu1->eta, mu1->pt, errSel);
          }

          if(mu2->q>0) {
            Double_t effdata = dataSelEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataSelEff_pos.getErrLow(mu2->eta, mu2->pt), dataSelEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmSelEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmSelEff_pos.getErrLow(mu2->eta, mu2->pt), zmmSelEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t errSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hSelErr_pos->Fill(mu2->eta, mu2->pt, errSel);
          } else {
            Double_t effdata = dataSelEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataSelEff_neg.getErrLow(mu2->eta, mu2->pt), dataSelEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmSelEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmSelEff_neg.getErrLow(mu2->eta, mu2->pt), zmmSelEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t errSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hSelErr_neg->Fill(mu2->eta, mu2->pt, errSel);
	  }

	  //HLT
          if(mu1->q>0) {
            Double_t effdata = dataHLTEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataHLTEff_pos.getErrLow(mu1->eta, mu1->pt), dataHLTEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmHLTEff_pos.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmHLTEff_pos.getErrLow(mu1->eta, mu1->pt), zmmHLTEff_pos.getErrHigh(mu1->eta, mu1->pt));
            Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hHLTErr_pos->Fill(mu1->eta, mu1->pt, errHLT);
          } else {
            Double_t effdata = dataHLTEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errdata = TMath::Max(dataHLTEff_neg.getErrLow(mu1->eta, mu1->pt), dataHLTEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t effmc   = zmmHLTEff_neg.getEff(mu1->eta, mu1->pt);
            Double_t errmc   = TMath::Max(zmmHLTEff_neg.getErrLow(mu1->eta, mu1->pt), zmmHLTEff_neg.getErrHigh(mu1->eta, mu1->pt));
            Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hHLTErr_neg->Fill(mu1->eta, mu1->pt, errHLT);
          }

          if(mu2->q>0) {
            Double_t effdata = dataHLTEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataHLTEff_pos.getErrLow(mu2->eta, mu2->pt), dataHLTEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmHLTEff_pos.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmHLTEff_pos.getErrLow(mu2->eta, mu2->pt), zmmHLTEff_pos.getErrHigh(mu2->eta, mu2->pt));
            Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hHLTErr_pos->Fill(mu2->eta, mu2->pt, errHLT);
          } else {
            Double_t effdata = dataHLTEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errdata = TMath::Max(dataHLTEff_neg.getErrLow(mu2->eta, mu2->pt), dataHLTEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t effmc   = zmmHLTEff_neg.getEff(mu2->eta, mu2->pt);
            Double_t errmc   = TMath::Max(zmmHLTEff_neg.getErrLow(mu2->eta, mu2->pt), zmmHLTEff_neg.getErrHigh(mu2->eta, mu2->pt));
            Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
            hHLTErr_neg->Fill(mu2->eta, mu2->pt, 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;
      }
    }
    for(Int_t iy=0; iy<=hSelErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hSelErr_pos->GetNbinsX(); ix++) {
        Double_t err=hSelErr_pos->GetBinContent(ix,iy);
        var+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hSelErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hSelErr_neg->GetNbinsX(); ix++) {
        Double_t err=hSelErr_neg->GetBinContent(ix,iy);
        var+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hTrkErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hTrkErr_pos->GetNbinsX(); ix++) {
        Double_t err=hTrkErr_pos->GetBinContent(ix,iy);
        //var+=err*err;
	var+=0.0;
      }
    }
    for(Int_t iy=0; iy<=hTrkErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hTrkErr_neg->GetNbinsX(); ix++) {
        Double_t err=hTrkErr_neg->GetBinContent(ix,iy);
	var+=0.0;
        //var+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hStaErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hStaErr_pos->GetNbinsX(); ix++) {
        Double_t err=hStaErr_pos->GetBinContent(ix,iy);
	var+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hStaErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hStaErr_neg->GetNbinsX(); ix++) {
        Double_t err=hStaErr_neg->GetBinContent(ix,iy);
	var+=err*err;
      }
    }

    nSelCorrVarv[ifile]+=var;

    // compute acceptances
    accv.push_back(nSelv[ifile]/nEvtsv[ifile]);     accErrv.push_back(accv[ifile]*sqrt((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 muonArr;

    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================    
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> mu mu" << 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 -> mu mu" << 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("computeAccSelZmmBinned"); 
}
예제 #4
0
void sideSigZpMMu(std::string inputFile, std::string outName){

  TreeReader data(inputFile.data());

  // variable bin
  const Double_t varBins[] = {680,720,760,800,840,920,1000,1100,
			      1250,1400,1600,1800,2000,2400};

  Int_t nvarBins = sizeof(varBins)/sizeof(varBins[0])-1;

  TH1D* h_ZpMass[2]; // [side,signal]
  std::string htitle[2] = {"Side-band region Zprime Mass","Signal region Zprime Mass"};

  for(Int_t i = 0; i < 2; i++){

    h_ZpMass[i] = new TH1D(Form("h_sideZprimeMass%d",i), "", nvarBins, varBins);
    h_ZpMass[i]->SetTitle(htitle[i].data());
    h_ZpMass[i]->GetXaxis()->SetTitle("Zprime mass");
    h_ZpMass[i]->GetYaxis()->SetTitle("Event number");

  }

  // begin of event loop
  for (Long64_t ev = 0; ev < data.GetEntriesFast(); ev++){

    if ( ev % 100000 == 0 )
      fprintf(stderr, "Processing event %lli of %lli\n", ev + 1, data.GetEntriesFast());
    data.GetEntry(ev);

    Float_t* CA8jetPt   = data.GetPtrFloat("CA8jetPt");
    Float_t* CA8jetEta  = data.GetPtrFloat("CA8jetEta");
    Float_t* CA8jetPhi  = data.GetPtrFloat("CA8jetPhi");
    Float_t* CA8jetMass = data.GetPtrFloat("CA8jetMass");
    Float_t* CA8jetPrunedMass = data.GetPtrFloat("CA8jetPrunedMass");
    Float_t* muPt  = data.GetPtrFloat("muPt");
    Float_t* muEta = data.GetPtrFloat("muEta");
    Float_t* muPhi = data.GetPtrFloat("muPhi");
    Float_t* muM   = data.GetPtrFloat("muM");
    Float_t* elePt = data.GetPtrFloat("elePt");

    // data trigger cut
    if ( outName.find("DoubleMu") != std::string::npos ){

      std::string* trigName = data.GetPtrString("hlt_trigName");
      Int_t* trigResult = data.GetPtrInt("hlt_trigResult");
      const Int_t nsize = data.GetPtrStringSize();

      Bool_t passTrigger = false;

      for(int it = 0; it < nsize; it++){

	std::string thisTrig = trigName[it];
	Int_t results = trigResult[it];

	// muon channel
	if( thisTrig.find("HLT_Mu22_TkMu8") != std::string::npos && results == 1 ){

	  passTrigger = true;
	  break;

	}

      }
   
      if( !passTrigger ) continue;

    }
     
    // pass muon ID
    Int_t stRecoMuIndex, ndRecoMuIndex;
    if( !passMuonID(data, &stRecoMuIndex, &ndRecoMuIndex) )
      continue;
    
    // reconstruct Z mass
    TLorentzVector stRecoMu, ndRecoMu;  
 
    stRecoMu.SetPtEtaPhiM(muPt[stRecoMuIndex], 
			  muEta[stRecoMuIndex], 
			  muPhi[stRecoMuIndex],
			  muM[stRecoMuIndex]);  
  
    ndRecoMu.SetPtEtaPhiM(muPt[ndRecoMuIndex], 
			  muEta[ndRecoMuIndex],
			  muPhi[ndRecoMuIndex], 
			  muM[ndRecoMuIndex]); 
    
    TLorentzVector Z = stRecoMu + ndRecoMu;
    
    // pass boosted-jet ID
    Int_t mode = 2; // 1:b-tagging only, 2:tau21 only
    Int_t maxJetIndex;

    if( !passJetID(data, mode, &maxJetIndex) )
      continue;
 
    TLorentzVector Higgs;
    Higgs.SetPtEtaPhiM(CA8jetPt[maxJetIndex],
		       CA8jetEta[maxJetIndex],
		       CA8jetPhi[maxJetIndex],
		       CA8jetMass[maxJetIndex]);

    if( Z.E() <= 1e-6 || Higgs.E() <= 1e-6 ) continue;
    if( Z.M() <= 70 || Z.M() >=110 ) continue;
    if( Z.Pt() <= 80 ) continue;

    // side band region
    if( CA8jetPrunedMass[maxJetIndex] > 50 && CA8jetPrunedMass[maxJetIndex] < 110 ){

      TLorentzVector Zprime = Z + Higgs; 
      h_ZpMass[0]->Fill(Zprime.M());

    }
    
    // signal region
    if( CA8jetPrunedMass[maxJetIndex] > 110 && CA8jetPrunedMass[maxJetIndex] < 140 ){

      TLorentzVector Zprime = Z + Higgs; 
      h_ZpMass[1]->Fill(Zprime.M());
   
    }
    
  } // end of event loop

  fprintf(stderr, "Processed all events\n");

  // output file
  TFile* outFile = new TFile("sideSigZpMMu.root", "update");

  std::string writeName[2] = {"sideZpMass_"+outName.substr(11),
			      "signZpMass_"+outName.substr(11)};

  for(Int_t i = 0; i < 2; i++){
    h_ZpMass[i]->Write(writeName[i].data());
  }

  outFile->Write();
  
}
예제 #5
0
void muIsolation(std::string inputFile) {

    TreeReader data(inputFile.data());

    gStyle->SetOptStat(0);

    // Declare the histogram

    TH1D* h_DeltaR = new TH1D("h_DeltaR", "Delta R", 100, 0, 2);
    TH1D* h_DeltaR_beforeIsoCut = (TH1D*)h_DeltaR->Clone("h_DeltaR_beforeIsoCut");
    TH1D* h_DeltaR_TrkIsoCut = (TH1D*)h_DeltaR->Clone("h_DeltaR_TrkIsoCut");

    TH1D* h_Vtx = new TH1D("h_Vtx", "info_nVtx", 100, 0, 50);
    TH1D* h_Vtx_beforeIsoCut = (TH1D*)h_Vtx->Clone("h_Vtx_beforeIsoCut");
    TH1D* h_Vtx_TrkIsoCut = (TH1D*)h_Vtx->Clone("h_Vtx_TrkIsoCut");

    TH1D* h_Iso = new TH1D("h_Iso", "Isolation variables", 100, 0, 1000);
    TH1D* h_muCorrTrkIso = (TH1D*)h_Iso->Clone("h_muCorrTrkIso");
    TH1D* h_muTrkIso = (TH1D*)h_Iso->Clone("h_muTrkIso");


    // Declare the histogram of efficiency

    TGraphAsymmErrors* h_EffIsoCut = new TGraphAsymmErrors();
    h_EffIsoCut->SetMarkerColor(4);
    h_EffIsoCut->SetMarkerStyle(20);
    h_EffIsoCut->SetMarkerSize(0.5);

    TGraphAsymmErrors* h_Eff_TrkIsoCut = (TGraphAsymmErrors*)h_EffIsoCut->Clone("h_Eff_TrkIsoCut");
    TGraphAsymmErrors* h_Eff_VtxTrkIsoCut = (TGraphAsymmErrors*)h_EffIsoCut->Clone("h_Eff_VtxTrkIsoCut");


    // begin of event loop

    for (Long64_t ev = 0; ev < data.GetEntriesFast(); ev++) {

        if ( ev % 50000 == 0 )
            fprintf(stderr, "Processing event %lli of %lli\n", ev + 1, data.GetEntriesFast());
        data.GetEntry(ev);

        Int_t info_nVtx = data.GetInt("info_nVtx");
        Float_t* muPt  = data.GetPtrFloat("muPt");
        Float_t* muEta = data.GetPtrFloat("muEta");
        Float_t* muPhi = data.GetPtrFloat("muPhi");
        Float_t* muM   = data.GetPtrFloat("muM");
        Float_t* muTrkIso = data.GetPtrFloat("muTrkIso");
        Float_t* muCorrTrkIso = data.GetPtrFloat("muCorrTrkIso");

        Int_t stRecoMuIndex, ndRecoMuIndex;

        // please check if the isolation cut in this function has been comment or not
        if( !passMuonID(data, &stRecoMuIndex, &ndRecoMuIndex) )
            continue;


        TLorentzVector stRecoMu, ndRecoMu;
        stRecoMu.SetPtEtaPhiM(muPt[stRecoMuIndex], muEta[stRecoMuIndex], muPhi[stRecoMuIndex], muM[stRecoMuIndex]);
        ndRecoMu.SetPtEtaPhiM(muPt[ndRecoMuIndex], muEta[ndRecoMuIndex], muPhi[ndRecoMuIndex], muM[ndRecoMuIndex]);

        h_DeltaR_beforeIsoCut->Fill(stRecoMu.DeltaR(ndRecoMu));
        h_DeltaR_beforeIsoCut->Fill(stRecoMu.DeltaR(ndRecoMu));

        h_Vtx_beforeIsoCut->Fill(info_nVtx);
        h_Vtx_beforeIsoCut->Fill(info_nVtx);


        // if pass muCorrTrkIso cut

        Double_t cutValue = 0.1;

        if( ( muCorrTrkIso[stRecoMuIndex] / muPt[stRecoMuIndex] ) < cutValue ) {
            h_DeltaR_TrkIsoCut->Fill(stRecoMu.DeltaR(ndRecoMu));
            h_Vtx_TrkIsoCut->Fill(info_nVtx);
        }

        if( ( muCorrTrkIso[ndRecoMuIndex] / muPt[ndRecoMuIndex] ) < cutValue ) {
            h_DeltaR_TrkIsoCut->Fill(stRecoMu.DeltaR(ndRecoMu));
            h_Vtx_TrkIsoCut->Fill(info_nVtx);
        }

        // fill isolation variables

        h_muCorrTrkIso->Fill(muCorrTrkIso[stRecoMuIndex]);
        h_muCorrTrkIso->Fill(muCorrTrkIso[ndRecoMuIndex]);

        h_muTrkIso->Fill(muTrkIso[stRecoMuIndex]);
        h_muTrkIso->Fill(muTrkIso[ndRecoMuIndex]);

    }

    // end of event loop

    // -- START of TCanvas -- //

    TCanvas* c_allMuEff = new TCanvas("c_allMuEff", "", 0, 0, 1920, 560);
    c_allMuEff->Divide(3,1);

    // -- plot muCorrTrkIso efficiency as a function of Delta R -- //

    c_allMuEff->cd(1);
    h_Eff_TrkIsoCut->Divide(h_DeltaR_TrkIsoCut, h_DeltaR_beforeIsoCut);
    h_Eff_TrkIsoCut->SetTitle("muCorrTrkIso");
    h_Eff_TrkIsoCut->GetXaxis()->SetTitle("Delta R");
    h_Eff_TrkIsoCut->GetYaxis()->SetTitle("Iso/Pt cut Efficiency");
    h_Eff_TrkIsoCut->Draw();

    // -- plot muCorrTrkIso efficiency as a function of nVtx -- //

    c_allMuEff->cd(2);
    h_Eff_VtxTrkIsoCut->Divide(h_Vtx_TrkIsoCut, h_Vtx_beforeIsoCut);
    h_Eff_VtxTrkIsoCut->SetTitle("muCorrVtxTrkIso");
    h_Eff_VtxTrkIsoCut->GetXaxis()->SetTitle("nVtx");
    h_Eff_VtxTrkIsoCut->GetYaxis()->SetTitle("Iso/Pt cut Efficiency");
    h_Eff_VtxTrkIsoCut->Draw();

    // -- plot the distribution of each isolation variable -- //

    c_allMuEff->cd(3)->SetLogy();

    h_muCorrTrkIso->SetFillStyle(3001);
    h_muCorrTrkIso->SetFillColor(kBlue);
    h_muCorrTrkIso->SetLineColor(kBlack);
    h_muCorrTrkIso->Scale(1./h_muCorrTrkIso->Integral(0,101));
    h_muCorrTrkIso->GetXaxis()->SetTitle("Isolation");
    h_muCorrTrkIso->GetYaxis()->SetTitle("Normalize distribution");
    h_muCorrTrkIso->Draw();

    h_muTrkIso->SetFillStyle(3001);
    h_muTrkIso->SetFillColor(kGreen);
    h_muTrkIso->SetLineColor(kBlack);
    h_muTrkIso->Scale(1./h_muTrkIso->Integral(0,101));
    h_muTrkIso->GetXaxis()->SetTitle("Isolation");
    h_muTrkIso->GetYaxis()->SetTitle("Normalize distribution");
    h_muTrkIso->Draw("same");

    TLegend *leg = new TLegend(0.5, 0.75, 0.9, 0.9);
    leg->SetFillStyle(1001);
    leg->SetFillColor(10);
    leg->SetBorderSize(1);
    leg->AddEntry(h_muCorrTrkIso, "muCorrTrkIso");
    leg->AddEntry(h_muTrkIso, "muTrkIso");
    leg->Draw();

    // -- END of TCanvas -- //

    fprintf(stderr, "Processed all events\n");

    c_allMuEff->Print("muIsoCutEff_qqllM2000.gif");


}
예제 #6
0
void selectZmm(const TString conf="zmm.conf", // input file
               const TString outputDir=".",   // output directory
	       const Bool_t  doScaleCorr=0,    // apply energy scale corrections
	       const Bool_t  doPU=0
) {
  gBenchmark->Start("selectZmm");

  //--------------------------------------------------------------------------------------------------------------
  // 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.4;
  const Double_t MUON_MASS = 0.105658369;

  const Int_t BOSON_ID  = 23;
  const Int_t LEPTON_ID = 13;

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

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

  TH1D *h_rw = (TH1D*) f_rw->Get("puWeights");
  TH1D *h_rw_up = (TH1D*) f_rw->Get("puWeightsUp");
  TH1D *h_rw_down = (TH1D*) f_rw->Get("puWeightsDown");


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

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

  enum { eMuMu2HLT=1, eMuMu1HLT1L1, eMuMu1HLT, eMuMuNoSel, eMuSta, eMuTrk };  // 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;
  ///// muon specific /////
  Float_t trkIso1, emIso1, hadIso1, trkIso2, emIso2, hadIso2;
  Float_t pfChIso1, pfGamIso1, pfNeuIso1, pfCombIso1, pfChIso2, pfGamIso2, pfNeuIso2, pfCombIso2;
  Float_t d01, dz1, d02, dz2;
  Float_t muNchi21,  muNchi22;
  UInt_t nPixHits1, nTkLayers1, nPixHits2, nTkLayers2;
  UInt_t nValidHits1, nMatch1, nValidHits2, nMatch2;
  UInt_t typeBits1, typeBits2;
  TLorentzVector *sta1=0, *sta2=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 *muonArr    = new TClonesArray("baconhep::TMuon");
  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 "zmm" - flag to store GEN Z kinematics
    Bool_t isSignal = (snamev[isam].CompareTo("zmm",TString::kIgnoreCase)==0);
    // flag to reject Z->mm 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 (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("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
    ///// muon 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 combined isolation of tag lepton
    outTree->Branch("pfCombIso2",  &pfCombIso2,  "pfCombIso2/F");    // PF combined isolation of probe lepton    
    outTree->Branch("d01",         &d01,         "d01/F");           // transverse impact parameter of tag lepton
    outTree->Branch("d02",         &d02,         "d02/F");           // transverse impact parameter of probe lepton	 
    outTree->Branch("dz1",         &dz1,         "dz1/F");           // longitudinal impact parameter of tag lepton
    outTree->Branch("dz2",         &dz2,         "dz2/F");           // longitudinal impact parameter of probe lepton	 
    outTree->Branch("muNchi21",    &muNchi21,    "muNchi21/F");      // muon fit normalized chi^2 of tag lepton
    outTree->Branch("muNchi22",    &muNchi22,    "muNchi22/F");      // muon fit normalized chi^2 of probe lepton
    outTree->Branch("nPixHits1",   &nPixHits1,	 "nPixHits1/i");     // number of pixel hits of tag muon
    outTree->Branch("nPixHits2",   &nPixHits2,	 "nPixHits2/i");     // number of pixel hits of probe muon
    outTree->Branch("nTkLayers1",  &nTkLayers1,  "nTkLayers1/i");    // number of tracker layers of tag muon
    outTree->Branch("nTkLayers2",  &nTkLayers2,  "nTkLayers2/i");    // number of tracker layers of probe muon
    outTree->Branch("nMatch1",     &nMatch1,	 "nMatch1/i");       // number of matched segments of tag muon
    outTree->Branch("nMatch2",     &nMatch2,	 "nMatch2/i");       // number of matched segments of probe muon 
    outTree->Branch("nValidHits1", &nValidHits1, "nValidHits1/i");   // number of valid muon hits of tag muon
    outTree->Branch("nValidHits2", &nValidHits2, "nValidHits2/i");   // number of valid muon hits of probe muon
    outTree->Branch("typeBits1",   &typeBits1,   "typeBits1/i");     // muon type of tag muon
    outTree->Branch("typeBits2",   &typeBits2,   "typeBits2/i");     // muon type of probe muon
    outTree->Branch("sta1",        "TLorentzVector", &sta1);         // tag standalone muon 4-vector
    outTree->Branch("sta2",        "TLorentzVector", &sta2);         // probe standalone muon 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);
      if (samp->fnamev[ifile] == "/dev/null") 
	      {
	     	cout <<"-> Ignoring null input "<<endl; 
		continue;
	      }


      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("Muon", &muonArr);   TBranch *muonBr = eventTree->GetBranch("Muon");
      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 = doPU ? h_rw->GetBinContent(h_rw->FindBin(info->nPUmean)) : 1.;
	  puWeightUp = doPU ? h_rw_up->GetBinContent(h_rw_up->FindBin(info->nPUmean)) : 1.;
	  puWeightDown = doPU ? h_rw_down->GetBinContent(h_rw_down->FindBin(info->nPUmean)) : 1.;
	  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 = doPU ? h_rw->GetBinContent(h_rw->FindBin(info->nPUmean)) : 1.;
	  puWeightUp = doPU ? h_rw_up->GetBinContent(h_rw_up->FindBin(info->nPUmean)) : 1.;
	  puWeightDown = doPU ? h_rw_down->GetBinContent(h_rw_down->FindBin(info->nPUmean)) : 1.;
	  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 = doPU ? h_rw->GetBinContent(h_rw->FindBin(info->nPUmean)) : 1.;
	  puWeightUp = doPU ? h_rw_up->GetBinContent(h_rw_up->FindBin(info->nPUmean)) : 1.;
	  puWeightDown = doPU ? h_rw_down->GetBinContent(h_rw_down->FindBin(info->nPUmean)) : 1.;
	  weight*=gen->weight*puWeight;
	  weightUp*=gen->weight*puWeightUp;
	  weightDown*=gen->weight*puWeightDown;
	}

	// veto z -> xx decays for signal and z -> mm 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 (!isMuonTrigger(triggerMenu, info->triggerBits)) continue;

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

	muonArr->Clear();
        muonBr->GetEntry(ientry);

	TLorentzVector vTag(0,0,0,0);
	TLorentzVector vTagSta(0,0,0,0);
	Double_t tagPt=0;
	Double_t Pt1=0;
	Double_t Pt2=0;
	Int_t itag=-1;
	
        for(Int_t i1=0; i1<muonArr->GetEntriesFast(); i1++) {
          const baconhep::TMuon *tag = (baconhep::TMuon*)((*muonArr)[i1]);

          // apply scale and resolution corrections to MC
          Double_t tagpt_corr = tag->pt;
          if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0)
            tagpt_corr = gRandom->Gaus(tag->pt*getMuScaleCorr(tag->eta,0),getMuResCorr(tag->eta,0));
	
	  if(tagpt_corr     < PT_CUT)        continue;  // lepton pT cut
	  if(fabs(tag->eta) > ETA_CUT)       continue;  // lepton |eta| cut
	  if(!passMuonID(tag))               continue;  // lepton selection

	  double Mu_Pt=0;
	  if(doScaleCorr) {
	    Mu_Pt=gRandom->Gaus(tag->pt*getMuScaleCorr(tag->eta,0),getMuResCorr(tag->eta,0));
	  }
	  else
	    {
	      Mu_Pt=tag->pt;
	    }

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

          if(!isMuonTriggerObj(triggerMenu, tag->hltMatchBits, kFALSE)) continue;

	  if(Mu_Pt<tagPt) continue;

	  tagPt=Mu_Pt;
	  itag=i1;
        
          // apply scale and resolution corrections to MC
          if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0) {
            vTag.SetPtEtaPhiM(tagpt_corr,tag->eta,tag->phi,MUON_MASS);
            vTagSta.SetPtEtaPhiM(gRandom->Gaus(tag->staPt*getMuScaleCorr(tag->eta,0),getMuResCorr(tag->eta,0)),tag->staEta,tag->staPhi,MUON_MASS);
          } else {
            vTag.SetPtEtaPhiM(tag->pt,tag->eta,tag->phi,MUON_MASS);
            vTagSta.SetPtEtaPhiM(tag->staPt,tag->staEta,tag->staPhi,MUON_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 - 
						   0.5*(tag->puIso),Double_t(0));
	  d01         = tag->d0;
	  dz1         = tag->dz;
	  muNchi21    = tag->muNchi2;
	  nPixHits1   = tag->nPixHits;
	  nTkLayers1  = tag->nTkLayers;
	  nMatch1     = tag->nMatchStn;
	  nValidHits1 = tag->nValidHits;
	  typeBits1   = tag->typeBits;
	  q1 = tag->q;
	}

	if(tagPt<Pt2) continue;

	TLorentzVector vProbe(0,0,0,0); TLorentzVector vProbeSta(0,0,0,0);
	Double_t probePt=0;
	Int_t passID=false;
	UInt_t icat=0;

	for(Int_t i2=0; i2<muonArr->GetEntriesFast(); i2++) {
	  if(itag==i2) continue;
	  const baconhep::TMuon *probe = (baconhep::TMuon*)((*muonArr)[i2]);
	  

	  // apply scale and resolution corrections to MC
	  Double_t probept_corr = probe->pt;
	  if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0)
	    probept_corr = gRandom->Gaus(probe->pt*getMuScaleCorr(probe->eta,0),getMuResCorr(probe->eta,0));

	  if(probept_corr     < PT_CUT)  continue;  // lepton pT cut
	  if(fabs(probe->eta) > ETA_CUT) continue;  // lepton |eta| cut

	  double Mu_Pt=probept_corr;
  
	  if(passID&&passMuonID(probe)&&Mu_Pt<probePt) continue;
	  if(passID&&!passMuonID(probe)) continue;
	  if(!passID&&!passMuonID(probe)&&Mu_Pt<probePt) continue;

	  if(!passID&&passMuonID(probe)) passID=true;

	  probePt=Mu_Pt;

	  // apply scale and resolution corrections to MC
	  if(doScaleCorr && snamev[isam].CompareTo("data",TString::kIgnoreCase)!=0) {
	    vProbe.SetPtEtaPhiM(probept_corr,probe->eta,probe->phi,MUON_MASS);
	    if(probe->typeBits & baconhep::EMuType::kStandalone)
	      vProbeSta.SetPtEtaPhiM(gRandom->Gaus(probe->staPt*getMuScaleCorr(probe->eta,0),getMuResCorr(probe->eta,0)),probe->staEta,probe->staPhi,MUON_MASS);
	  } else {
	    vProbe.SetPtEtaPhiM(probe->pt,probe->eta,probe->phi,MUON_MASS);
	    if(probe->typeBits & baconhep::EMuType::kStandalone)
	      vProbeSta.SetPtEtaPhiM(probe->staPt,probe->staEta,probe->staPhi,MUON_MASS);
	  }

	  trkIso2     = probe->trkIso;
	  emIso2      = probe->ecalIso;
	  hadIso2     = probe->hcalIso;
	  pfChIso2    = probe->chHadIso;
	  pfGamIso2   = probe->gammaIso;
	  pfNeuIso2   = probe->neuHadIso;
	  pfCombIso2  = probe->chHadIso + TMath::Max(probe->neuHadIso + probe->gammaIso - 
						     0.5*(probe->puIso),Double_t(0));
	  d02         = probe->d0;
	  dz2         = probe->dz;
	  muNchi22    = probe->muNchi2;
	  nPixHits2   = probe->nPixHits;
	  nTkLayers2  = probe->nTkLayers;
	  nMatch2     = probe->nMatchStn;
	  nValidHits2 = probe->nValidHits;
	  typeBits2   = probe->typeBits;
	  q2 = probe->q;

	  // determine event category
	  if(passMuonID(probe)) {
	    if(isMuonTriggerObj(triggerMenu, probe->hltMatchBits, kFALSE)) {
	      icat=eMuMu2HLT;
	    }
	    else if(isMuonTriggerObj(triggerMenu, probe->hltMatchBits, kTRUE)) {
	      icat=eMuMu1HLT1L1;
	  }
	    else {
	      icat=eMuMu1HLT;
	    }
	  }
	  else if(probe->typeBits & baconhep::EMuType::kGlobal) { icat=eMuMuNoSel; }
	  else if(probe->typeBits & baconhep::EMuType::kStandalone) { icat=eMuSta; }
	  else if(probe->nTkLayers>=6 && probe->nPixHits>=1)        { icat=eMuTrk; }
	}
	
	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.5) ||
			    ((glep2) && toolbox::deltaR(vTag.Eta(), vTag.Phi(), glep2->Eta(), glep2->Phi())<0.5) );
	  
	  Bool_t match2 = ( ((glep1) && toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), glep1->Eta(), glep1->Phi())<0.5) ||
			    ((glep2) && toolbox::deltaR(vProbe.Eta(), vProbe.Phi(), glep2->Eta(), glep2->Phi())<0.5) );

	  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;
	sta1        = &vTagSta;
	sta2        = &vProbeSta;
	
	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, sta1=0, sta2=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 h_rw_up;
  delete h_rw_down;
  delete f_rw;
  delete info;
  delete gen;
  delete genPartArr;
  delete muonArr;
  delete vertexArr;
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  cout << " Z -> mu mu" << 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("selectZmm"); 
}
void computeAccSelWm_Inclusive_Bin(const TString conf,       // input file
		     const TString inputDir,
                     const TString outputDir,  // output directory
		     const Int_t   charge,      // 0 = inclusive, +1 = W+, -1 = W-
		     const Int_t   doPU
) {
  gBenchmark->Start("computeAccSelWm");

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

  const Double_t PT_CUT     = 25;
  const Double_t ETA_CUT    = 2.4;
  const Double_t ETA_BARREL = 1.2;
  const Double_t ETA_ENDCAP = 1.2;

  const Double_t VETO_PT   = 10;
  const Double_t VETO_ETA  = 2.4;

  const Int_t BOSON_ID  = 24;
  const Int_t LEPTON_ID = 13;
  
  // efficiency files
  const TString dataHLTEffName_pos = inputDir + "MuHLTEff/1MGpositive/eff.root";
  const TString dataHLTEffName_neg = inputDir + "MuHLTEff/1MGnegative/eff.root";
  const TString zmmHLTEffName_pos  = inputDir + "MuHLTEff/1CTpositive/eff.root";
  const TString zmmHLTEffName_neg  = inputDir + "MuHLTEff/1CTnegative/eff.root";

  const TString dataSelEffName_pos = inputDir + "MuSITEff/1MGpositive/eff.root";
  const TString dataSelEffName_neg = inputDir + "MuSITEff/1MGnegative/eff.root";
  const TString zmmSelEffName_pos  = inputDir + "MuSITEff/1CTpositive/eff.root";
  const TString zmmSelEffName_neg  = inputDir + "MuSITEff/1CTnegative/eff.root";

  const TString dataTrkEffName_pos = inputDir + "MuSITEff/1MGpositive/eff.root";
  const TString dataTrkEffName_neg = inputDir + "MuSITEff/1MGnegative/eff.root";
  const TString zmmTrkEffName_pos  = inputDir + "MuSITEff/1CTpositive/eff.root";
  const TString zmmTrkEffName_neg  = inputDir + "MuSITEff/1CTnegative/eff.root";

  const TString dataStaEffName_pos = inputDir + "MuStaEff/1MGpositive/eff.root";
  const TString dataStaEffName_neg = inputDir + "MuStaEff/1MGnegative/eff.root";
  const TString zmmStaEffName_pos  = inputDir + "MuStaEff/1CTpositive/eff.root";
  const TString zmmStaEffName_neg  = inputDir + "MuStaEff/1CTnegative/eff.root";

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


  //--------------------------------------------------------------------------------------------------------------
  // 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
  //
  TH2D *h=0;

  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 *zmmHLTEffFile_pos = new TFile(zmmHLTEffName_pos);
  CEffUser2D zmmHLTEff_pos;
  zmmHLTEff_pos.loadEff((TH2D*)zmmHLTEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmHLTEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmHLTEffFile_pos->Get("hErrhEtaPt"));

  TFile *zmmHLTEffFile_neg = new TFile(zmmHLTEffName_neg);
  CEffUser2D zmmHLTEff_neg;
  zmmHLTEff_neg.loadEff((TH2D*)zmmHLTEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmHLTEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmHLTEffFile_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());
  TH2D *hHLTErrB_pos = new TH2D("hHLTErrB_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hHLTErrB_neg = new TH2D("hHLTErrB_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hHLTErrE_pos = new TH2D("hHLTErrE_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hHLTErrE_neg = new TH2D("hHLTErrE_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());


  cout << "Loading selection efficiencies..." << endl;

  TFile *dataSelEffFile_pos = new TFile(dataSelEffName_pos);
  CEffUser2D dataSelEff_pos;
  dataSelEff_pos.loadEff((TH2D*)dataSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataSelEffFile_pos->Get("hErrhEtaPt"));

  TFile *dataSelEffFile_neg = new TFile(dataSelEffName_neg);
  CEffUser2D dataSelEff_neg;
  dataSelEff_neg.loadEff((TH2D*)dataSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataSelEffFile_neg->Get("hErrhEtaPt"));

  TFile *zmmSelEffFile_pos = new TFile(zmmSelEffName_pos);
  CEffUser2D zmmSelEff_pos;
  zmmSelEff_pos.loadEff((TH2D*)zmmSelEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmSelEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmSelEffFile_pos->Get("hErrhEtaPt"));

  TFile *zmmSelEffFile_neg = new TFile(zmmSelEffName_neg);
  CEffUser2D zmmSelEff_neg;
  zmmSelEff_neg.loadEff((TH2D*)zmmSelEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmSelEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmSelEffFile_neg->Get("hErrhEtaPt"));

  h =(TH2D*)dataSelEffFile_pos->Get("hEffEtaPt");
  TH2D *hSelErr_pos = new TH2D("hSelErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hSelErr_neg = new TH2D("hSelErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hSelErrB_pos = new TH2D("hSelErrB_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hSelErrB_neg = new TH2D("hSelErrB_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hSelErrE_pos = new TH2D("hSelErrE_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hSelErrE_neg = new TH2D("hSelErrE_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());


  cout << "Loading standalone efficiencies..." << endl;

  TFile *dataStaEffFile_pos = new TFile(dataStaEffName_pos);
  CEffUser2D dataStaEff_pos;
  dataStaEff_pos.loadEff((TH2D*)dataStaEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataStaEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataStaEffFile_pos->Get("hErrhEtaPt"));

  TFile *dataStaEffFile_neg = new TFile(dataStaEffName_neg);
  CEffUser2D dataStaEff_neg;
  dataStaEff_neg.loadEff((TH2D*)dataStaEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataStaEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataStaEffFile_neg->Get("hErrhEtaPt"));

  TFile *zmmStaEffFile_pos = new TFile(zmmStaEffName_pos);
  CEffUser2D zmmStaEff_pos;
  zmmStaEff_pos.loadEff((TH2D*)zmmStaEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmStaEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmStaEffFile_pos->Get("hErrhEtaPt"));

  TFile *zmmStaEffFile_neg = new TFile(zmmStaEffName_neg);
  CEffUser2D zmmStaEff_neg;
  zmmStaEff_neg.loadEff((TH2D*)zmmStaEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmStaEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmStaEffFile_neg->Get("hErrhEtaPt"));

  h =(TH2D*)dataStaEffFile_pos->Get("hEffEtaPt");
  TH2D *hStaErr_pos = new TH2D("hStaErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hStaErr_neg = new TH2D("hStaErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hStaErrB_pos = new TH2D("hStaErrB_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hStaErrB_neg = new TH2D("hStaErrB_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hStaErrE_pos = new TH2D("hStaErrE_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hStaErrE_neg = new TH2D("hStaErrE_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());


  cout << "Loading track efficiencies..." << endl;

  TFile *dataTrkEffFile_pos = new TFile(dataTrkEffName_pos);
  CEffUser2D dataTrkEff_pos;
  dataTrkEff_pos.loadEff((TH2D*)dataTrkEffFile_pos->Get("hEffEtaPt"), (TH2D*)dataTrkEffFile_pos->Get("hErrlEtaPt"), (TH2D*)dataTrkEffFile_pos->Get("hErrhEtaPt"));

  TFile *dataTrkEffFile_neg = new TFile(dataTrkEffName_neg);
  CEffUser2D dataTrkEff_neg;
  dataTrkEff_neg.loadEff((TH2D*)dataTrkEffFile_neg->Get("hEffEtaPt"), (TH2D*)dataTrkEffFile_neg->Get("hErrlEtaPt"), (TH2D*)dataTrkEffFile_neg->Get("hErrhEtaPt"));

  TFile *zmmTrkEffFile_pos = new TFile(zmmTrkEffName_pos);
  CEffUser2D zmmTrkEff_pos;
  zmmTrkEff_pos.loadEff((TH2D*)zmmTrkEffFile_pos->Get("hEffEtaPt"), (TH2D*)zmmTrkEffFile_pos->Get("hErrlEtaPt"), (TH2D*)zmmTrkEffFile_pos->Get("hErrhEtaPt"));

  TFile *zmmTrkEffFile_neg = new TFile(zmmTrkEffName_neg);
  CEffUser2D zmmTrkEff_neg;
  zmmTrkEff_neg.loadEff((TH2D*)zmmTrkEffFile_neg->Get("hEffEtaPt"), (TH2D*)zmmTrkEffFile_neg->Get("hErrlEtaPt"), (TH2D*)zmmTrkEffFile_neg->Get("hErrhEtaPt"));

  h =(TH2D*)dataTrkEffFile_pos->Get("hEffEtaPt");
  TH2D *hTrkErr_pos = new TH2D("hTrkErr_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hTrkErr_neg = new TH2D("hTrkErr_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hTrkErrB_pos = new TH2D("hTrkErrB_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hTrkErrB_neg = new TH2D("hTrkErrB_neg", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hTrkErrE_pos = new TH2D("hTrkErrE_pos", "",h->GetNbinsX(),h->GetXaxis()->GetXmin(),h->GetXaxis()->GetXmax(),
                               h->GetNbinsY(),h->GetYaxis()->GetXmin(),h->GetYaxis()->GetXmax());
  TH2D *hTrkErrE_neg = new TH2D("hTrkErrE_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        *muonArr = new TClonesArray("baconhep::TMuon");
  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("Muon",          &muonArr); TBranch *muonBr    = eventTree->GetBranch("Muon"); 
    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);
   
    //
    // loop over events
    //    
    for(UInt_t ientry=0; ientry<eventTree->GetEntries(); ientry++) {
    //for(UInt_t ientry=0; ientry<1000000; ientry++) {
      genBr->GetEntry(ientry);
      infoBr->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;
      /*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,1);*/
      
      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 (!isMuonTrigger(triggerMenu, info->triggerBits)) continue;
   
      // good vertex requirement
      if(!(info->hasGoodPV)) continue;
    
      muonArr->Clear();
      muonBr->GetEntry(ientry);
      Int_t nLooseLep=0;
      const baconhep::TMuon *goodMuon=0;
      Bool_t passSel=kFALSE;
      for(Int_t i=0; i<muonArr->GetEntriesFast(); i++) {
  	const baconhep::TMuon *mu = (baconhep::TMuon*)((*muonArr)[i]);

        if(fabs(mu->eta) > VETO_ETA) continue; // loose lepton |eta| cut
        if(mu->pt	 < VETO_PT)  continue; // loose lepton pT cut
        if(passMuonLooseID(mu)) nLooseLep++;   // loose lepton selection
        if(nLooseLep>1) {  // extra lepton veto
          passSel=kFALSE;
          break;
        }
        
        if(fabs(mu->eta) > ETA_CUT)         continue;  // lepton |eta| cut
        if(mu->pt < PT_CUT)		    continue;  // lepton pT cut	
        if(!passMuonID(mu))		    continue;  // lepton selection
	if(!isMuonTriggerObj(triggerMenu, mu->hltMatchBits, kFALSE)) continue;
	
	if(charge!=0 && mu->q!=charge) continue;  // check charge (if necessary)
	
	passSel=kTRUE;
	goodMuon=mu;
      }
      
      if(passSel) {
        
	/******** We have a W candidate! HURRAY! ********/
        
	Bool_t isBarrel = (fabs(goodMuon->eta)<ETA_BARREL) ? kTRUE : kFALSE;
        
	// data/MC scale factor corrections
         Double_t effdata, effmc;
         Double_t corr=1;

         effdata=1; effmc=1;
         if(goodMuon->q>0) {
           effdata *= dataHLTEff_pos.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmHLTEff_pos.getEff(goodMuon->eta, goodMuon->pt);
         } else {
           effdata *= dataHLTEff_neg.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmHLTEff_neg.getEff(goodMuon->eta, goodMuon->pt);
         }
	 corr *= effdata/effmc;

         effdata=1; effmc=1;
         if(goodMuon->q>0) {
           effdata *= dataSelEff_pos.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmSelEff_pos.getEff(goodMuon->eta, goodMuon->pt);
         } else {
           effdata *= dataSelEff_neg.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmSelEff_neg.getEff(goodMuon->eta, goodMuon->pt);
         }
         corr *= effdata/effmc;

         effdata=1; effmc=1;
         if(goodMuon->q>0) {
           effdata *= dataStaEff_pos.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmStaEff_pos.getEff(goodMuon->eta, goodMuon->pt);
         } else {
           effdata *= dataStaEff_neg.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmStaEff_neg.getEff(goodMuon->eta, goodMuon->pt);
         }
         corr *= effdata/effmc;

         effdata=1; effmc=1;
         if(goodMuon->q>0) {
           effdata *= dataTrkEff_pos.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmTrkEff_pos.getEff(goodMuon->eta, goodMuon->pt);
         } else {
           effdata *= dataTrkEff_neg.getEff(goodMuon->eta, goodMuon->pt);
           effmc   *= zmmTrkEff_neg.getEff(goodMuon->eta, goodMuon->pt);
         }
         //corr *= effdata/effmc;

	// scale factor uncertainties
	// TRACKER
        if(goodMuon->q>0) {
          Double_t effdata = dataTrkEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataTrkEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), dataTrkEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmTrkEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmTrkEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), zmmTrkEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errTrk = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hTrkErr_pos->Fill(goodMuon->eta, goodMuon->pt, errTrk);
          if(isBarrel) hTrkErrB_pos->Fill(goodMuon->eta,goodMuon->pt,errTrk);
          else         hTrkErrE_pos->Fill(goodMuon->eta,goodMuon->pt,errTrk);
        } else {
          Double_t effdata = dataTrkEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataTrkEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), dataTrkEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmTrkEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmTrkEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), zmmTrkEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errTrk = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hTrkErr_neg->Fill(goodMuon->eta, goodMuon->pt, errTrk);
          if(isBarrel) hTrkErrB_neg->Fill(goodMuon->eta,goodMuon->pt,errTrk);
          else         hTrkErrE_neg->Fill(goodMuon->eta,goodMuon->pt,errTrk);
        }

	// STANDALONE
        if(goodMuon->q>0) {
          Double_t effdata = dataStaEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataStaEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), dataStaEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmStaEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmStaEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), zmmStaEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errSta = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hStaErr_pos->Fill(goodMuon->eta, goodMuon->pt, errSta);
	  if(isBarrel) hStaErrB_pos->Fill(goodMuon->eta,goodMuon->pt,errSta);
	  else	       hStaErrE_pos->Fill(goodMuon->eta,goodMuon->pt,errSta);
        } else {
          Double_t effdata = dataStaEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataStaEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), dataStaEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmStaEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmStaEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), zmmStaEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errSta = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hStaErr_neg->Fill(goodMuon->eta, goodMuon->pt, errSta);
          if(isBarrel) hStaErrB_neg->Fill(goodMuon->eta,goodMuon->pt,errSta);
          else         hStaErrE_neg->Fill(goodMuon->eta,goodMuon->pt,errSta);
        }

	// SELECTION
        if(goodMuon->q>0) {
          Double_t effdata = dataSelEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataSelEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), dataSelEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmSelEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmSelEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), zmmSelEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hSelErr_pos->Fill(goodMuon->eta, goodMuon->pt, errSel);
          if(isBarrel) hSelErrB_pos->Fill(goodMuon->eta,goodMuon->pt,errSel);
          else         hSelErrE_pos->Fill(goodMuon->eta,goodMuon->pt,errSel);
        } else {
          Double_t effdata = dataSelEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataSelEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), dataSelEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmSelEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmSelEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), zmmSelEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errSel = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hSelErr_neg->Fill(goodMuon->eta, goodMuon->pt, errSel);
          if(isBarrel) hSelErrB_neg->Fill(goodMuon->eta,goodMuon->pt,errSel);
          else         hSelErrE_neg->Fill(goodMuon->eta,goodMuon->pt,errSel);
        }

	//HLT
        if(goodMuon->q>0) {
          Double_t effdata = dataHLTEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataHLTEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), dataHLTEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmHLTEff_pos.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmHLTEff_pos.getErrLow(goodMuon->eta, goodMuon->pt), zmmHLTEff_pos.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hHLTErr_pos->Fill(goodMuon->eta, goodMuon->pt, errHLT);
          if(isBarrel) hHLTErrB_pos->Fill(goodMuon->eta,goodMuon->pt,errHLT);
          else         hHLTErrE_pos->Fill(goodMuon->eta,goodMuon->pt,errHLT);
        } else {
          Double_t effdata = dataHLTEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errdata = TMath::Max(dataHLTEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), dataHLTEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t effmc   = zmmHLTEff_neg.getEff(goodMuon->eta, goodMuon->pt);
          Double_t errmc   = TMath::Max(zmmHLTEff_neg.getErrLow(goodMuon->eta, goodMuon->pt), zmmHLTEff_neg.getErrHigh(goodMuon->eta, goodMuon->pt));
          Double_t errHLT = (effdata/effmc)*sqrt(errdata*errdata/effdata/effdata + errmc*errmc/effmc/effmc);
          hHLTErr_neg->Fill(goodMuon->eta, goodMuon->pt, errHLT);
          if(isBarrel) hHLTErrB_neg->Fill(goodMuon->eta,goodMuon->pt,errHLT);
          else         hHLTErrE_neg->Fill(goodMuon->eta,goodMuon->pt,errHLT);
        }

        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_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hHLTErr_pos->GetNbinsX(); ix++) {
        Double_t err;
	err=hHLTErr_pos->GetBinContent(ix,iy);  var+=err*err;
        err=hHLTErrB_pos->GetBinContent(ix,iy); varB+=err*err;
        err=hHLTErrE_pos->GetBinContent(ix,iy); varE+=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;
        err=hHLTErr_neg->GetBinContent(ix,iy);  var+=err*err;
        err=hHLTErrB_neg->GetBinContent(ix,iy); varB+=err*err;
        err=hHLTErrE_neg->GetBinContent(ix,iy); varE+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hSelErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hSelErr_pos->GetNbinsX(); ix++) {
        Double_t err;
        err=hSelErr_pos->GetBinContent(ix,iy);  var+=err*err;
        err=hSelErrB_pos->GetBinContent(ix,iy); varB+=err*err;
        err=hSelErrE_pos->GetBinContent(ix,iy); varE+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hSelErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hSelErr_neg->GetNbinsX(); ix++) {
        Double_t err;
        err=hSelErr_neg->GetBinContent(ix,iy);  var+=err*err;
        err=hSelErrB_neg->GetBinContent(ix,iy); varB+=err*err;
        err=hSelErrE_neg->GetBinContent(ix,iy); varE+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hTrkErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hTrkErr_pos->GetNbinsX(); ix++) {
        Double_t err;
        err=hTrkErr_pos->GetBinContent(ix,iy);  var+=0.0;//err*err;
        err=hTrkErrB_pos->GetBinContent(ix,iy); varB+=0.0;//err*err;
        err=hTrkErrE_pos->GetBinContent(ix,iy); varE+=0.0;//err*err;
      }
    }
    for(Int_t iy=0; iy<=hTrkErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hTrkErr_neg->GetNbinsX(); ix++) {
        Double_t err;
        err=hTrkErr_neg->GetBinContent(ix,iy);  var+=0.0;//err*err;
        err=hTrkErrB_neg->GetBinContent(ix,iy); varB+=0.0;//err*err;
        err=hTrkErrE_neg->GetBinContent(ix,iy); varE+=0.0;//err*err;
      }
    }
    for(Int_t iy=0; iy<=hStaErr_pos->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hStaErr_pos->GetNbinsX(); ix++) {
        Double_t err;
        err=hStaErr_pos->GetBinContent(ix,iy);  var+=err*err;
        err=hStaErrB_pos->GetBinContent(ix,iy); varB+=err*err;
        err=hStaErrE_pos->GetBinContent(ix,iy); varE+=err*err;
      }
    }
    for(Int_t iy=0; iy<=hStaErr_neg->GetNbinsY(); iy++) {
      for(Int_t ix=0; ix<=hStaErr_neg->GetNbinsX(); ix++) {
        Double_t err;
        err=hStaErr_neg->GetBinContent(ix,iy);  var+=err*err;
        err=hStaErrB_neg->GetBinContent(ix,iy); varB+=err*err;
        err=hStaErrE_neg->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 muonArr;
  
    
  //--------------------------------------------------------------------------------------------------------------
  // Output
  //==============================================================================================================
   
  cout << "*" << endl;
  cout << "* SUMMARY" << endl;
  cout << "*--------------------------------------------------" << endl;
  if(charge== 0) cout << " W -> mu nu"  << endl;
  if(charge==-1) cout << " W- -> mu nu" << endl;
  if(charge== 1) cout << " W+ -> mu 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 -> mu nu"  << endl;
  if(charge==-1) txtfile << " W- -> mu nu" << endl;
  if(charge== 1) txtfile << " W+ -> mu 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("computeAccSelWm"); 
}