file analyses/Analysis_ATLAS_13TeV_2OSLEP_Z_139invfb.cpp

[No description available]

Namespaces

Name
Gambit
TODO: see if we can use this one:
Gambit::ColliderBit

Classes

Name
classGambit::ColliderBit::Analysis_ATLAS_13TeV_2OSLEP_Z_139invfb

Source code

///
///  \author Tomas Gonzalo
///  \date 2019 June
///  *********************************************

// Based on https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/CONFNOTES/ATLAS-CONF-2019-016/

// - 139 fb^-1 data

#include <vector>
#include <cmath>
#include <memory>
#include <iomanip>
#include <algorithm>
#include <fstream>

#include "gambit/ColliderBit/analyses/Analysis.hpp"
#include "gambit/ColliderBit/ATLASEfficiencies.hpp"
#include "gambit/ColliderBit/analyses/Cutflow.hpp"
#include "gambit/ColliderBit/mt2_bisect.h"

// #define CHECK_CUTFLOW

using namespace std;

namespace Gambit
{
  namespace ColliderBit
  {

    class Analysis_ATLAS_13TeV_2OSLEP_Z_139invfb : public Analysis
    {

    protected:

      // Counters for the number of accepted events for each signal region
      std::map<string, EventCounter> _counters = {
        {"SR1A", EventCounter("SR1A")},
        {"SR1B", EventCounter("SR1B")},
        {"SR2A", EventCounter("SR2A")},
        {"SR2B", EventCounter("SR2B")},
      };

       vector<Cutflow> _cutflow;

       //vector<int> _test;
       //int _test2;

    private:

      struct ptComparison
      {
        bool operator() (const HEPUtils::Particle* i,const HEPUtils::Particle* j) {return (i->pT()>j->pT());}
      } comparePt;

      struct ptJetComparison
      {
        bool operator() (const HEPUtils::Jet* i,const HEPUtils::Jet* j) {return (i->pT()>j->pT());}
      } compareJetPt;


    public:

      // Required detector sim
      static constexpr const char* detector = "ATLAS";

      Analysis_ATLAS_13TeV_2OSLEP_Z_139invfb()
      {

        set_analysis_name("ATLAS_13TeV_2OSLEP_Z_139invfb");
        set_luminosity(139);


        str cutflow_name = "ATLAS 2 opposite sign leptons at the Z peak 13 TeV";
        vector<str> SR1A = {"Trigger", "Third leading lepton pT > 20 GeV", "|mll - mZ| < 15 GeV", "nb-tagged (pT > 30 GeV) >= 1", "njets (pT > 30 GeV) >= 4", "MET > 250 GeV", "mT23l > 100 GeV"};
        vector<str> SR1B = {"Trigger", "Third leading lepton pT > 20 GeV", "|mll - mZ| < 15 GeV", "nb-tagged (pT > 30 GeV) >= 1", "njets (pT > 30 GeV) >= 5", "MET > 150 GeV", "pTll > 150 GeV", "Leading b-tagged jet pT > 100 GeV"};
        vector<str> SR2A = {"Trigger", "Third leading lepton pT < 20 GeV", "|mll - mZ| < 15 GeV", "Leading jet pT > 150 GeV", "MET > 200 GeV", "pTll < 50 GeV"};
        vector<str> SR2B = {"Trigger", "Third leading lepton pT < 60 GeV", "|mll - mZ| < 15 GeV", "nb-tagged (pT > 30 GeV) >= 1", "MET > 350 GeV", "pTll > 150 GeV"};
        _cutflow = { Cutflow(cutflow_name, SR1A),
                     Cutflow(cutflow_name, SR1B),
                     Cutflow(cutflow_name, SR2A),
                     Cutflow(cutflow_name, SR2B) };
        //_test = {0,0,0,0,0};
        //_test2 = 0;

      }

      void run(const HEPUtils::Event* event)
      {

        // Baseline objects
        vector<const HEPUtils::Particle*> baselineElectrons;
        vector<const HEPUtils::Particle*> baselineMuons;
        vector<const HEPUtils::Particle*> baselineTaus;
        vector<const HEPUtils::Jet*> baselineJets;
        vector<const HEPUtils::Jet*> baselineBJets;
        vector<const HEPUtils::Jet*> baselineNonBJets;

        // Missing momentum and energy
        HEPUtils::P4 ptot = event->missingmom();
        double met = event->met();

        //if(event->electrons().size() + event->muons().size() >= 3)
        //  _test2++;

        // Initialize cutflow
        for(int i=0; i<4; i++)
          _cutflow[i].fillinit();

        // Electron candidates are reconstructed from isolated electromagnetic calorimeter energy deposits matched to ID tracks and are required to have |η| < 2.47, a transverse momentum pT > 4.5 GeV, and to pass the “LooseAndBLayer” requirement in arXiv: 1902.04655 [hep-ex].
        for (const HEPUtils::Particle* electron : event->electrons())
        {
          if (electron->pT()>4.5 && electron->abseta()<2.47) baselineElectrons.push_back(electron);
        }

        // Apply electron efficiency
        // Loose electron ID selection
        ATLAS::applyElectronIDEfficiency2019(baselineElectrons, "Loose");

        // Muon candidates are reconstructed in the region |η| < 2.4 from muon spectrometer tracks matching ID tracks. Candidate muons must have pT > 4 GeV and pass the medium identification requirements defined in arXiv: 1603.05598 [hep-ex].
        for (const HEPUtils::Particle* muon : event->muons())
        {
          if (muon->pT()>4. && muon->abseta()<2.4) baselineMuons.push_back(muon);
        }

        // Apply muon efficiency
        // Missing: "Medium" muon ID criteria
        ATLAS::applyMuonEff(baselineMuons);

        // Missing: transverse and longitudinal impact parameter cuts


        // Only jet candidates with pT > 20 GeV and |η| < 2.8 are considered in the analysis
        // Jets with pT < 120 GeV and |η| < 2.8 have an efficiency of 90%
        // Mising:  cut based on detector noise and non-collision backgrounds
        double jet_eff = 0.9;
        for (const HEPUtils::Jet* jet : event->jets())
        {
          if (jet->pT()>20. && jet->abseta()<2.8)
            if( (jet->pT() >= 120. || jet->abseta() >= 2.5) || random_bool(jet_eff) ) baselineJets.push_back(jet);
        }

        // Overlap removal

        // 1) Remove jets within DeltaR = 0.2 of electron
        // If b-tagging efficiency > 85%, do not remove jet. The lepton will be removed anyway.
        removeOverlap(baselineJets, baselineElectrons, 0.2, false, 200, 0.85);

        // 3) Remove jets within DeltaR = 0.2 of a muon
        removeOverlap(baselineJets, baselineMuons, 0.2, false, DBL_MAX, 0.85);

        // 2) Remove electrons within DeltaR = 0.4 of a jet
        removeOverlap(baselineElectrons, baselineJets, 0.4);

        // 4) Remove muons within DeltaR = 0.4 of jet
        // Use lambda function to remove overlap with DeltaRMax as min(0.4, 0.04 + pT(µ)/10 GeV)
        // Missing: Remove the jet instead if the jet has fewer than 3 associated tracks
        auto lambda = [](double muonpT) { return std::min(0.4, 0.04 + muonpT/10.); };
        removeOverlap(baselineMuons, baselineJets, lambda);

        // 5) Remove electron candidates sharing and ID track with a muon candidate
        // Missing: No track information

        // Find b-jets
        // Copied from ATLAS_13TeV_3b_24invfb
        double btag = 0.77; double cmisstag = 1/16.; double misstag = 1./113.;
        for (const HEPUtils::Jet* jet : baselineJets) {
          // Tag
          if( jet->btag() && random_bool(btag) ) baselineBJets.push_back(jet);
          // Misstag c-jet
          else if( jet->ctag() && random_bool(cmisstag) ) baselineBJets.push_back(jet);
          // Misstag light jet
          else if( random_bool(misstag) ) baselineBJets.push_back(jet);
          // Non b-jet
          else baselineNonBJets.push_back(jet);
        }

        // Signal objects
        vector<const HEPUtils::Jet*> signalJets = baselineJets;
        vector<const HEPUtils::Jet*> signalBJets = baselineBJets;
        vector<const HEPUtils::Particle*> signalElectrons = baselineElectrons;
        vector<const HEPUtils::Particle*> signalMuons;
        vector<const HEPUtils::Particle*> signalLeptons;

        // Signal electrons must satisfy the “medium” identification requirement as defined in arXiv: 1902.04655 [hep-ex]
        ATLAS::applyElectronIDEfficiency2019(signalElectrons, "Medium");


        // Signal muons must have pT > 5 GeV.
        for (const HEPUtils::Particle* signalMuon : baselineMuons)
        {
          if (signalMuon->pT() > 5.) signalMuons.push_back(signalMuon);
        }

        // Missing: we need track information for isolation criteria for signal leptons

        // Fill signal leptons
        signalLeptons = signalElectrons;
        signalLeptons.insert(signalLeptons.end(), signalMuons.begin(), signalMuons.end());

        // Sort by pT
        sort(signalJets.begin(), signalJets.end(), compareJetPt);
        sort(signalBJets.begin(), signalBJets.end(), compareJetPt);
        sort(signalLeptons.begin(), signalLeptons.end(), comparePt);

        // Trigger requirements are
        // - >=3 signal leptons
        // - >=1 SF-OS pair
        // - leading lepton pT > 40 GeV
        // - subleading lepton pT > 20 GeV
        // - Zlike, |mll - mZ| < 15 GeV
        bool preselection = false;

        // Count signal leptons and jets
        size_t nSignalLeptons = signalLeptons.size();
        size_t nSignalJets = signalJets.size();
        size_t nSignalBJets = signalBJets.size();

        // Get SFOS pairs
        vector<vector<const HEPUtils::Particle*>> SFOSpairs = getSFOSpairs(signalLeptons);

        // Get SFOS pairs masses and pTs
        vector<double> SFOSpair_masses;
        vector<double> SFOSpair_pTs;
        for (vector<const HEPUtils::Particle*> pair : SFOSpairs)
        {
          SFOSpair_masses.push_back( (pair.at(0)->mom() + pair.at(1)->mom()).m() );
          SFOSpair_pTs.push_back( (pair.at(0)->mom() + pair.at(1)->mom()).pT() );
        }
        std::sort(SFOSpair_masses.begin(), SFOSpair_masses.end(), std::greater<double>());
        std::sort(SFOSpair_pTs.begin(), SFOSpair_pTs.end(), std::greater<double>());

        // Z resonance
        bool Zlike = false;
        double mZ = 91.2;
        for(double m : SFOSpair_masses)
        {
          if (abs(m - mZ) < 15)
            Zlike = true;
        }

        // Combine all preselection cuts
        preselection = nSignalLeptons >= 3 && SFOSpairs.size() >= 1 && signalLeptons.at(0)->pT() > 40. && signalLeptons.at(1)->pT() > 20. && Zlike;

        // Construct the mT23l variable for the pair of SFOS with invariant mass closest to mZ and highest pT lepton not in the pair
        vector<const HEPUtils::Particle*> SFOSpairClosestToMZ;
        double mll =  0;
        // Find the SFOS pair high inv mass closest to mZ
        for (vector<const HEPUtils::Particle*> pair: SFOSpairs)
        {
          if( fabs( (pair.at(0)->mom() + pair.at(1)->mom()).m() - mZ ) < fabs(mll - mZ) )
          {
            mll = (pair.at(0)->mom() + pair.at(1)->mom()).m();
            SFOSpairClosestToMZ = pair;
          }
        }

        // Construct the pTll variable
        double pTll = 0.0;
        if(SFOSpairClosestToMZ.size() == 2)
          pTll = ( SFOSpairClosestToMZ.at(0)->mom() + SFOSpairClosestToMZ.at(1)->mom() ).pT();

        // Find the highest pT lepton not in the pair, but make sure there are at least 3 leptons
        double mT23l = 0.0;
        if(nSignalLeptons >= 3 and SFOSpairClosestToMZ.size() == 2)
        {
          const HEPUtils::Particle* thirdLepton;
          if(signalLeptons.at(0) != SFOSpairClosestToMZ.at(0) && signalLeptons.at(0) != SFOSpairClosestToMZ.at(1))
            thirdLepton = signalLeptons.at(0);
          else if(signalLeptons.at(1) != SFOSpairClosestToMZ.at(0) && signalLeptons.at(1) != SFOSpairClosestToMZ.at(1))
            thirdLepton = signalLeptons.at(1);
          else
            thirdLepton = signalLeptons.at(2);

          double pa[3] = { mll, (SFOSpairClosestToMZ.at(0)->mom() + SFOSpairClosestToMZ.at(1)->mom()).px(), (SFOSpairClosestToMZ.at(0)->mom() + SFOSpairClosestToMZ.at(1)->mom()).py() };
          double pb[3] = { 0, thirdLepton->mom().px(), thirdLepton->mom().py() };
          double pmiss[3] = { met, ptot.px(), ptot.py() };
          double mn = 0.;

          mt2_bisect::mt2 mt2_calc;
          mt2_calc.set_momenta(pa,pb,pmiss);
          mt2_calc.set_mn(mn);
          mT23l = mt2_calc.get_mt2();
        }

        // Signal Regions

        // Requirement                      SR1A     SR1B    SR2A    SR2B
        // ---------------------------------------------------------------
        // Third leading lepton pT           >20      >20     <20     <60   // done
        // njets (pT > 30 GeV)               >=4      >=5     >=3     >=3   // done
        // nb-tagged jets (pT > 30 GeV)      >=1      >=1      -      >=1   // done
        // Leading jet pT                     -        -     >150      -    // done
        // Leading b-tagged jet pT            -      >100      -       -    // done
        // MET                              >250     >150    >200    >350   // done
        // pTll                               -      >150     <50    >150   // done
        // mT23l                            >100       -       -       -    // done

        // SR1A
        if (preselection &&
            signalLeptons.at(2)->pT() > 20. &&
            nSignalJets >= 4 && signalJets.at(3)->pT() > 30. &&
            nSignalBJets >= 1 && signalBJets.at(0)->pT() > 30. &&
            // -
            // -
            met > 250. &&
            // -
            mT23l > 100.
           )
          _counters.at("SR1A").add_event(event);

        // SR1B
        if (preselection &&
            signalLeptons.at(2)->pT() > 20. &&
            nSignalJets >= 5 && signalJets.at(4)->pT() > 30. &&
            nSignalBJets >= 1 && signalBJets.at(0)->pT() > 30. &&
            // -
            signalBJets.at(0)->pT() > 100. &&
            met > 150. &&
            pTll > 150.
            // -
           )
          _counters.at("SR1B").add_event(event);

        // SR2A
        if (preselection &&
            signalLeptons.at(2)->pT() < 20. &&
            nSignalJets >= 3 && signalJets.at(2)->pT() > 30. &&
            // -
            signalJets.at(0)->pT() > 150. &&
            // -
            met > 200. &&
            pTll < 50.
            // -
           )
          _counters.at("SR2A").add_event(event);

        // SR2B
        if (preselection &&
            signalLeptons.at(2)->pT() < 60. &&
            nSignalJets >= 3 && signalJets.at(2)->pT() > 30. &&
            nSignalBJets >= 1 && signalBJets.at(0)->pT() > 30. &&
           // -
           // -
           met > 350. &&
           pTll > 150.
           // -
           )
          _counters.at("SR2B").add_event(event);

        // Cutflows

        // Fill cutflow with preselection trigger as defined by ATLAS
        //if(nSignalLeptons >= 3) _test[0]++;
        //if(nSignalLeptons >= 3 && nSignalJets >= 3 && signalJets.at(2)->pT() > 30.) _test[1]++;
        //if(nSignalLeptons >= 3 && nSignalJets >= 3 && signalJets.at(2)->pT() > 30. && met > 50.) _test[2]++;
        //if(nSignalLeptons >= 3 && nSignalJets >= 3 && signalJets.at(2)->pT() > 30. && met > 50. && signalLeptons.at(0)->pT() > 40.) _test[3]++;
        //if(nSignalLeptons >= 3 && nSignalJets >= 3 && signalJets.at(2)->pT() > 30. && met > 50. && signalLeptons.at(0)->pT() > 40. && signalLeptons.at(1)->pT() > 20.) _test[4]++;
        if(nSignalLeptons >= 3 && nSignalJets >= 3 && signalJets.at(2)->pT() > 30. && met > 50. && signalLeptons.at(0)->pT() > 40. && signalLeptons.at(1)->pT() > 20.)
        {
          // 1
          for(int i=0; i<4; i++)
            _cutflow[i].fill(1);

          bool SR[] ={true, true, true, true};

          // 2
          // Third leading lepton pT
          for(int i=0; i<2; i++)
            if(signalLeptons.at(2)->pT() > 20)
              _cutflow[i].fill(2);
            else
              SR[i] = false;
          if(signalLeptons.at(2)->pT() < 20)
            _cutflow[2].fill(2);
          else SR[2] = false;
          if(signalLeptons.at(2)->pT() < 60)
            _cutflow[3].fill(2);
          else SR[3] = false;

          // 3
          // Z peak
          for(int i=0; i<4; i++)
            if(Zlike)
             _cutflow[i].fill(3, SR[i]);
            else SR[i] = false;

          // 4
          // nbtagged jets (pT > 30 GeV)
          for(int i=0; i<4; i++)
            if(nSignalBJets >= 1 && signalBJets.at(0)->pT() > 30. && i != 2)
              _cutflow[i].fill(4, SR[i]);
            else if (i != 2)
              SR[i] = false;
          // Leading jet pT > 150 GeV
          if(signalJets.at(0)->pT() > 150.)
            _cutflow[2].fill(4, SR[2]);
          else SR[2] = false;

          // 5
          // n jets (pT > 30 GeV)
          if(nSignalJets >= 4 && signalJets.at(3)->pT() > 30.)
            _cutflow[0].fill(5, SR[0]);
          else SR[0] = false;
          if(nSignalJets >= 5 && signalJets.at(4)->pT() > 30.)
            _cutflow[1].fill(5, SR[1]);
          else SR[1] = false;
          // MET
          if(met > 200.)
            _cutflow[2].fill(5, SR[2]);
          else SR[2] = false;
          if(met > 350.)
            _cutflow[3].fill(5, SR[3]);
          else SR[3] = false;

          // 6
          // MET
          if(met > 250.)
            _cutflow[0].fill(6, SR[0]);
          else SR[0] = false;
          if(met > 150.)
            _cutflow[1].fill(6, SR[1]);
          else SR[1] = false;
          // pTll
          if(pTll < 50.)
            _cutflow[2].fill(6, SR[2]);
          else SR[2] = false;
          if(pTll > 150.)
            _cutflow[3].fill(6, SR[3]);
          else SR[3] = false;

          // 7
          // mT23l
          if(mT23l > 100.)
            _cutflow[0].fill(7, SR[0]);
          else SR[0] = false;
          // pTll
          if(pTll > 150.)
            _cutflow[1].fill(7, SR[1]);
          else SR[1] = false;

          // 8
          // Leading b-tagget jet pT > 100 GeV
          if(nSignalBJets >= 1 && signalBJets.at(0)->pT() > 100.)
            _cutflow[1].fill(8, SR[1]);
          else SR[1] = false;
        }
      }

      /// Combine the variables of another copy of this analysis (typically on another thread) into this one.
      void combine(const Analysis* other)
      {
        const Analysis_ATLAS_13TeV_2OSLEP_Z_139invfb* specificOther
                = dynamic_cast<const Analysis_ATLAS_13TeV_2OSLEP_Z_139invfb*>(other);

        for (auto& pair : _counters) { pair.second += specificOther->_counters.at(pair.first); }
      }

      // This function can be overridden by the derived SR-specific classes
      virtual void collect_results()
      {

        add_result(SignalRegionData(_counters.at("SR1A"), 3., {5.4, 0.7}));
        add_result(SignalRegionData(_counters.at("SR1B"), 14., {12.8, 1.6}));
        add_result(SignalRegionData(_counters.at("SR2A"), 3., {5.7, 1.7}));
        add_result(SignalRegionData(_counters.at("SR2B"), 6., {5.4, 0.8}));

        #ifdef CHECK_CUTFLOW
          cout << _cutflow << endl;
          //cout << "n signal leptons before = " << _test2 << endl;
          //cout << "n signal leptons = " << _test[0] << endl;
          //cout << "n signal jets (pT > 30) = " << _test[1] << endl;
          //cout << "met = " << _test[2] << endl;
          //cout << "leading lepton pT > 40 = " << _test[3] << endl;
          //cout << "subleading lepton pT > 20 = " << _test[4] << endl;
        #endif


      }


    protected:
      void analysis_specific_reset()
      {
        for (auto& pair : _counters) { pair.second.reset(); }
      }

    };

    // Factory fn
    DEFINE_ANALYSIS_FACTORY(ATLAS_13TeV_2OSLEP_Z_139invfb)


  }
}

Updated on 2022-08-03 at 12:57:58 +0000