Rivet analyses


title: ATLAS_2018_I1676551

Recursive jigsaw chargino-neutralino search with 2 or 3 charged leptons in 36/fb of 13 TeV pp

Experiment: ATLAS (LHC)

Inspire ID: 1676551

Status: VALIDATED NOTREENTRY SINGLEWEIGHT

Authors: - Derek Yeung - Andy Buckley

References: - Expt page: ATLAS-SUSY-2017-03 - Phys.Rev. D98 (2018) no.9, 092012 - DOI: 10.1103/PhysRevD.98.092012 - CERN-EP-2018-113

Beams: p+ p+

Beam energies: (6500.0, 6500.0)GeV

Run details: - BSM signal events, with 2 or 3 high-pT leptons.

A search for electroweak production of supersymmetric particles in two-lepton and three-lepton final states using recursive jigsaw reconstruction, a technique that assigns reconstructed objects to the most probable hemispheres of the decay trees, allowing one to construct tailored kinematic variables to separate the signal and background. The search uses data collected in 2015 and 2016 by the ATLAS experiment in $\sqrt{s}=13$~TeV proton-proton collisions at the CERN Large Hadron Collider corresponding to an integrated luminosity of 36.1/fb. Chargino-neutralino pair production, with decays via W/Z bosons, is studied in final states involving leptons and jets and missing transverse momentum for scenarios with large and intermediate mass splittings between the parent particle and lightest supersymmetric particle, as well as for the scenario where this mass splitting is close to the mass of the $Z$ boson. The latter case is challenging since the vector bosons are produced with kinematic properties that are similar to those in Standard Model processes. Results are found to be compatible with the Standard Model expectations in the signal regions targeting large and intermediate mass splittings, and chargino-neutralino masses up to 600~GeV are excluded at 95% confidence level for a massless lightest supersymmetric particle. Excesses of data above the expected background are found in the signal regions targeting low mass splittings, and the largest local excess amounts to 3.0 standard deviations.

Source code:ATLAS_2018_I1676551.cc

```c++ // -- C++ --

include "Rivet/Analysis.hh"

include "Rivet/Projections/DirectFinalState.hh"

include "Rivet/Projections/FastJets.hh"

include "Rivet/Projections/FinalState.hh"

include "Rivet/Projections/IndirectFinalState.hh"

include "Rivet/Projections/MissingMomentum.hh"

include "Rivet/Projections/Smearing.hh"

include "Rivet/Projections/TauFinder.hh"

include "Rivet/Tools/Cutflow.hh"

namespace Rivet {

/// Recursive jigsaw chargino-neutralino search with 2 or 3 charged leptons in 36/fb of 13 TeV pp /// /// @author Derek Yeung, Andy Buckley class ATLAS_2018_I1676551 : public Analysis { public:

/// Constructor
RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2018_I1676551);


/// Analysis initialization
void init() {

  PromptFinalState electrons(Cuts::abspid == PID::ELECTRON);
  SmearedParticles recoelectrons(electrons, ELECTRON_EFF_ATLAS_RUN2_MEDIUM, ELECTRON_SMEAR_ATLAS_RUN2);
  declare(recoelectrons, "Electrons");

  PromptFinalState muons(Cuts::abspid == PID::MUON);
  SmearedParticles recomuons(muons, MUON_EFF_ATLAS_RUN2_MEDIUM, MUON_SMEAR_ATLAS_RUN2);
  declare(recomuons, "Muons");

  FastJets jets4(IndirectFinalState(Cuts::open()), JetAlg::ANTIKT, 0.4);
  SmearedJets recojets4(jets4, JET_SMEAR_CMS_RUN2, JET_BTAG_EFFS(0.77, 1 / 6., 1 / 134.));
  declare(recojets4, "Jets");

  MissingMomentum met(FinalState(Cuts::abseta < 4.9));
  SmearedMET recomet(met, MET_SMEAR_ATLAS_RUN2);
  declare(recomet, "MET");

  // Cutflow Setup for 2l-High
  const strings edges = {"2l-high", "2l-int", "2l-low", "2l-ISR",
                         "3l-high", "3l-int", "3l-low", "3l-ISR"};
  book(_cutflows, edges);
  const strings cfnames1 = {"Trigger matching & 2 signal leptons",
                            "Preselection",
                            "Fraction 1 > 0.8",
                            "Fraction 2 < 0.05",
                            "Delta Phi in [0.3, 2.9]",
                            "H_PP_4,1 > 800 GeV"};
  const strings cfnames2 = {"Trigger matching & 2 signal leptons",
                            "Preselection",
                            "Fraction 1 > 0.8",
                            "Fraction 2 < 0.05",
                            "Delta Phi in [0.3, 2.6]",
                            "H_PP_4,1 > 600 GeV"};
  const strings cfnames3 = {"Trigger matching & 2 signal leptons",
                            "Preselection",
                            "Fraction 1 in [0.35, 0.6]",
                            "Fraction 2 < 0.05",
                            "Min Delta Phi > 2.4",
                            "H_PP_4,1 > 400 GeV"};
  const strings cfnames4 = {"Trigger matching & 2 signal leptons",
                            "Preselection",
                            "m_Z in [80, 100] GeV",
                            "m_J in [50, 110] GeV",
                            "Delta Phi > 2.8",
                            "R_ISR in [0.4, 0.75]",
                            "p_CM_T_ISR > 180 GeV",
                            "p_CM_T_I > 100 GeV",
                            "p_CM_T < 30 GeV"};
  const strings cfnames5 = {"Trigger matching & 3 signal leptons",
                            "Preselection",
                            "m_ll in [75,105] GeV",
                            "m_W_T > 150 GeV",
                            "Fraction 1 > 0.75",
                            "Fraction 2 < 0.8",
                            "H_PP_31 > 500 GeV",
                            "Fraction 3 < 0.2"};
  const strings cfnames6 = {"Trigger matching & 3 signal leptons",
                            "Preselection",
                            "m_ll in [75,105] GeV",
                            "m_W_T > 130 GeV",
                            "Fraction 1 > 0.8",
                            "Fraction 2 < 0.75",
                            "H_PP_31 > 450 GeV",
                            "Fraction 3 < 0.15"};
  const strings cfnames7 = {"Trigger matching & 3 signal leptons",
                            "Preselection",
                            "m_ll in [75,105] GeV",
                            "m_W_T > 100 GeV",
                            "Fraction 1 > 0.9",
                            "H_PP_31 > 250 GeV",
                            "Fraction 2 < 0.05"};
  const strings cfnames8 = {"Trigger matching & 3 signal leptons",
                            "Preselection",
                            "m_ll in [75, 105] GeV",
                            "m_W_T > 100 GeV",
                            "Delta Phi > 2.0",
                            "R_ISR in [0.55, 1.0]",
                            "p_CM_T_ISR > 100 GeV",
                            "p_CM_T_I > 80 GeV",
                            "p_CM_T < 25 GeV"};
  book(_cutflows->bin(1), edges[0], cfnames1); // Cutflow Setup for 2l-High
  book(_cutflows->bin(2), edges[1], cfnames2); // Cutflow Setup for 2l-Int
  book(_cutflows->bin(3), edges[2], cfnames3); // Cutflow Setup for 2l-Low
  book(_cutflows->bin(4), edges[3], cfnames4); // Cutflow Setup for 2l-ISR
  book(_cutflows->bin(5), edges[4], cfnames5); // Cutflow Setup for 3l-High
  book(_cutflows->bin(6), edges[5], cfnames6); // Cutflow Setup for 3l-Int
  book(_cutflows->bin(7), edges[6], cfnames7); // Cutflow Setup for 3l-Low
  book(_cutflows->bin(8), edges[7], cfnames8); // Cutflow Setup for 3l-ISR
}


// Per-event analysis
void analyze(const Event& event) {

  _cutflows->groupfillinit();

  // Obtain Electrons, Muons and Jets
  Particles elecs = apply<ParticleFinder>(event, "Electrons")
                        .particlesByPt(Cuts::pT > 10 * GeV && Cuts::abseta < 2.47);
  Particles muons = apply<ParticleFinder>(event, "Muons")
                        .particlesByPt(Cuts::pT > 10 * GeV && Cuts::abseta < 2.4);
  Particles leptons = sortByPt(elecs + muons);
  Jets jets = apply<SmearedJets>(event, "Jets").jetsByPt(Cuts::pT > 20 * GeV && Cuts::abseta < 2.4);

  // Discard jets within DR = 0.4 of prompt leptons
  idiscardIfAnyDeltaRLess(jets, leptons, 0.4);

  // 2-lepton High (n=0), Int (n=1) and Low (n=2) Selection
  for (size_t n = 0; n < _types.size(); ++n) {

    while (true) {

      // Require the leading electron and the leading muon to have pT > 25GeV
      if (elecs.size() != 0 && elecs[0].pT() < 25 * GeV) break;
      if (muons.size() != 0 && muons[0].pT() < 25 * GeV) break;

      // Obtain the missing transverse momentum vector
      Vector3 EtMissX = apply<SmearedMET>(event, "MET").vectorPt();
      Vector3 EtMiss = -EtMissX;

      // Require only 2 opposite charged, same flavoured leptons
      double n_leptons = leptons.size();
      if (n_leptons != 2) break;
      if (leptons[0].abspid() != leptons[1].abspid()) break;
      if (leptons[0].charge() * leptons[1].charge() >= 0) break;

      // Obtain the 4-momenta of leptons and implement requirements on them
      vector<FourMomentum> lepton;
      for (int l = 0; l < 2; ++l) lepton.push_back(leptons[l].mom());

      double p_l1_T = lepton[0].pT();
      if (p_l1_T < 25 * GeV) break;

      double p_l2_T = lepton[1].pT();
      if (p_l2_T < 25 * GeV) break;

      _cutflows->fillnext("2l"s + _types[n]);

      // Requirement on m_ll
      double m_ll = (lepton[0] + lepton[1]).mass();
      if (!inRange(m_ll, 80 * GeV, 100 * GeV)) break;

      // Obtain the 4-momenta of jets and implement requirements on them
      double n_jets = jets.size();
      if (n != 2) {
        if (n_jets < 2) break;
        if (any(jets, hasBTag())) break;
      }
      else {
        if (n_jets != 2) break;
        if (any(jets, hasBTag())) break;
      }

      vector<FourMomentum> jet;
      for (int l = 0; l < 2; ++l) jet.push_back(jets[l].momentum());

      double p_j1_T = jet[0].pT();
      if (p_j1_T < 30 * GeV) break;

      double p_j2_T = jet[1].pT();
      if (p_j2_T < 30 * GeV) break;

      double m_jj = (jet[0] + jet[1]).mass();
      if (n != 2) {
        if (m_jj < 60 * GeV || m_jj > 100 * GeV) break;
      }
      else {
        if (m_jj < 70 * GeV || m_jj > 90 * GeV) break;
      }

      // Pre-selection requirements cut
      _cutflows->fillnext("2l"s + _types[n]);

      // Invisible mass JR and Invisible Rapidity JR to obtain Invisible System 4-momentum
      FourMomentum P_V = lepton[0] + lepton[1] + jet[0] + jet[1];
      double M_I = sqrt(P_V.mass2() - 4 * m_ll * m_jj);
      double M_V = P_V.mass();
      double p_I_z = P_V.pz() * sqrt(EtMiss.dot(EtMiss) + sqr(M_I)) / sqrt(P_V.pT2() + sqr(M_V));

      FourMomentum P_I;
      P_I.setPM(EtMiss.x(), EtMiss.y(), p_I_z, M_I);

      // Lorentz boost to CM frame
      LorentzTransform LT = LorentzTransform::mkFrameTransform(P_I + P_V);

      FourMomentum P_F_I;   //4-momentum of invisible system in CM frame
      FourMomentum P_F_Va;  //4-momentum of visible system a in CM frame
      FourMomentum P_F_Vb;  //4-momentum of visible system b in CM frame
      FourMomentum P_F_V;   //4-momentum of visible system in CM frame
      FourMomentum P_F_Va1; //4-momentum of visible system a1 in CM frame
      FourMomentum P_F_Va2; //4-momentum of visible system a2 in CM frame
      FourMomentum P_F_Vb1; //4-momentum of visible system b1 in CM frame
      FourMomentum P_F_Vb2; //4-momentum of visible system b2 in CM frame
      if ((jet[0] + jet[1]).mass() > (lepton[0] + lepton[1]).mass()) {
        P_F_I = LT.transform(P_I);
        P_F_Va = LT.transform(jet[0] + jet[1]);
        P_F_Vb = LT.transform(lepton[0] + lepton[1]);
        P_F_V = LT.transform(P_V);
        //
        P_F_Va1 = LT.transform(jet[0]);
        P_F_Va2 = LT.transform(jet[1]);
        P_F_Vb1 = LT.transform(lepton[0]);
        P_F_Vb2 = LT.transform(lepton[1]);
      }
      else {
        P_F_I = LT.transform(P_I);
        P_F_Va = LT.transform(lepton[0] + lepton[1]);
        P_F_Vb = LT.transform(jet[0] + jet[1]);
        P_F_V = LT.transform(P_V);
        //
        P_F_Va1 = LT.transform(lepton[0]);
        P_F_Va2 = LT.transform(lepton[1]);
        P_F_Vb1 = LT.transform(jet[0]);
        P_F_Vb2 = LT.transform(jet[1]);
      }

      // Obtain variables defined in the Contra-boost Invariant JR
      double M2c = 2 * ((P_F_Va.E()) * (P_F_Vb.E()) + (P_F_Va.p3()).dot(P_F_Vb.p3()));
      double m2Va = sqr(P_F_Va.mass());
      double m2Vb = sqr(P_F_Vb.mass());
      double ka = m2Va - m2Vb + M2c - 2 * sqrt(m2Va) * sqrt(m2Vb);
      double kb = m2Vb - m2Va + M2c - 2 * sqrt(m2Va) * sqrt(m2Vb);
      double kn = ka * m2Va - kb * m2Vb + M2c * (kb - ka) / 2
          + (0.5) * sqrt(pow(ka + kb, 2) * (pow(M2c, 2) - 4 * m2Va * m2Vb));
      double k2d = sqr(ka) * m2Va + sqr(kb) * m2Vb + ka * kb * M2c;
      double k = kn / k2d;
      double ca = (1 + k * ka) / 2;
      double cb = (1 + k * kb) / 2;
      double c = 0.5 * (P_F_V.E() + sqrt(pow(P_F_V.E(), 2) + pow(M_I, 2) - pow(P_V.mass(), 2)))
          / (ca * (P_F_Va.E()) + cb * (P_F_Vb.E()));

      // Apply Contra-boost Invariant JR to obtain invisible particles' 4-momenta
      double p_F_Iax = (P_F_Va.px()) * (c * ca - 1) - (P_F_Vb.px()) * c * cb;
      double p_F_Iay = (P_F_Va.py()) * (c * ca - 1) - (P_F_Vb.py()) * c * cb;
      double p_F_Iaz = (P_F_Va.pz()) * (c * ca - 1) - (P_F_Vb.pz()) * c * cb;
      double p_F_Ibx = (P_F_Vb.px()) * (c * cb - 1) - (P_F_Va.px()) * c * ca;
      double p_F_Iby = (P_F_Vb.py()) * (c * cb - 1) - (P_F_Va.py()) * c * ca;
      double p_F_Ibz = (P_F_Vb.pz()) * (c * cb - 1) - (P_F_Va.pz()) * c * ca;
      double E_F_Ia = (c * ca - 1) * (P_F_Va.E()) + c * cb * (P_F_Vb.E());
      double E_F_Ib = (c * cb - 1) * (P_F_Vb.E()) + c * ca * (P_F_Va.E());

      FourMomentum P_F_Ia;
      FourMomentum P_F_Ib;
      P_F_Ia.setPE(p_F_Iax, p_F_Iay, p_F_Iaz, E_F_Ia);
      P_F_Ib.setPE(p_F_Ibx, p_F_Iby, p_F_Ibz, E_F_Ib);

      // Lorentz boost from the CM frame to the P_a and P_b frame
      LorentzTransform LTPa = LorentzTransform::mkFrameTransform(P_F_Va + P_F_Ia);
      LorentzTransform LTPb = LorentzTransform::mkFrameTransform(P_F_Vb + P_F_Ib);

      // min(H_Pa_11,H_Pb_11)/min(H_Pa_21,H_Pb,21) requirement
      if (n != 2) {
        double H_Pa_11 = (LTPa.transform(P_F_Va1 + P_F_Va2)).p() + (LTPa.transform(P_F_Ia)).p();
        double H_Pb_11 = (LTPb.transform(P_F_Vb1 + P_F_Vb2)).p() + (LTPb.transform(P_F_Ib)).p();
        double H_Pa_21 = (LTPa.transform(P_F_Va1)).p() + (LTPa.transform(P_F_Va2)).p()
            + (LTPa.transform(P_F_Ia)).p();
        double H_Pb_21 = (LTPa.transform(P_F_Vb1)).p() + (LTPb.transform(P_F_Vb2)).p()
            + (LTPb.transform(P_F_Ib)).p();
        vector<double> V1 = {H_Pa_11, H_Pb_11};
        vector<double> V2 = {H_Pa_21, H_Pb_21};
        double fraction1 = min(V1) / min(V2);
        if (fraction1 < 0.8) break;
      }
      else {
        double H_PP_41 = P_F_Va1.p() + P_F_Va2.p() + P_F_Vb1.p() + P_F_Vb2.p() + (P_F_Ia + P_F_Ib).p();
        double H_PP_11 = (P_F_Va1 + P_F_Va2 + P_F_Vb1 + P_F_Vb2).p() + (P_F_Ia + P_F_Ib).p();
        double fraction1 = H_PP_11 / H_PP_41;
        if (fraction1 < 0.35 || fraction1 > 0.6) break;
      }
      _cutflows->fillnext("2l"s + _types[n]);

      // Lorentz boost from the CM frame to the lab frame
      LorentzTransform LTR = LorentzTransform::mkObjTransform(P_I + P_V);

      // p_lab_T_PP/(p_lab_T_PP+p_lab_T_41) requirement
      double p_lab_T_PP = (LTR.transform(P_F_Va1 + P_F_Va2 + P_F_Vb1 + P_F_Vb2 + P_F_Ia + P_F_Ib)).pT();
      double H_PP_T_41 = P_F_Va1.pT() + P_F_Va2.pT() + P_F_Vb1.pT() + P_F_Vb2.pT()
          + (P_F_Ia + P_F_Ib).pT();
      double fraction2 = p_lab_T_PP / (p_lab_T_PP + H_PP_T_41);
      if (fraction2 > 0.05) break;
      _cutflows->fillnext("2l"s + _types[n]);

      // Delta-Phi requirement
      if (n == 0) {
        Vector3 vector1 = (P_F_Va + P_F_Ia).betaVec();
        Vector3 vector2 = (LTPa.transform(P_F_Va)).betaVec();
        if (deltaPhi(vector1, vector2) < 0.3 || deltaPhi(vector1, vector2) > 2.8) break;
        Vector3 vector3 = (P_F_Vb + P_F_Ib).betaVec();
        Vector3 vector4 = (LTPb.transform(P_F_Vb)).betaVec();
        if (deltaPhi(vector3, vector4) < 0.3 || deltaPhi(vector3, vector4) > 2.8) break;
      }
      else if (n == 1) {
        Vector3 vector1 = (P_F_Va + P_F_Ia).betaVec();
        Vector3 vector2 = (LTPa.transform(P_F_Va)).betaVec();
        if (deltaPhi(vector1, vector2) < 0.6 || deltaPhi(vector1, vector2) > 2.6) break;
        Vector3 vector3 = (P_F_Vb + P_F_Ib).betaVec();
        Vector3 vector4 = (LTPb.transform(P_F_Vb)).betaVec();
        if (deltaPhi(vector3, vector4) < 0.6 || deltaPhi(vector3, vector4) > 2.6) break;
      }
      else {
        Vector3 j1 = jet[0].p3();
        Vector3 j2 = jet[1].p3();
        double delta1 = deltaPhi(j1, EtMiss);
        double delta2 = deltaPhi(j2, EtMiss);
        double delta = min(delta1, delta2);
        if (delta < 2.4) break;
      }
      _cutflows->fillnext("2l"s + _types[n]);

      // H_PP_41 requirement
      double H_PP_41 = P_F_Va1.p() + P_F_Va2.p() + P_F_Vb1.p() + P_F_Vb2.p() + (P_F_Ia + P_F_Ib).p();
      if (n == 0) {
        if (H_PP_41 < 800 * GeV) break;
      }
      else if (n == 1) {
        if (H_PP_41 < 600 * GeV) break;
      }
      else {
        if (H_PP_41 < 400 * GeV) break;
      }
      _cutflows->fillnext("2l"s + _types[n]);

      break;
    }
  }


  // 2-lepton ISR Selection
  while (true) {

    // Require the leading electron and the leading muon to have pT > 25 GeV
    if (elecs.size() != 0 && elecs[0].pT() < 25 * GeV) break;
    if (muons.size() != 0 && muons[0].pT() < 25 * GeV) break;

    // Require only 2 opposite charged, same flavoured leptons
    double n_leptons = leptons.size();
    if (n_leptons != 2) break;
    if (leptons[0].abspid() != leptons[1].abspid()) break;
    if (leptons[0].charge() * leptons[1].charge() >= 0) break;

    // Obtain the 4-momenta of leptons and implement requirements on them
    vector<FourMomentum> lepton;
    double M;
    for (int n = 0; n < 2; ++n) {
      lepton.push_back(leptons[n].mom());
      M = lepton[n].mass();
      lepton[n].setPz(0);
      lepton[n].setE(sqrt(pow(lepton[n].px(), 2) + pow(lepton[n].py(), 2) + pow(M, 2)));
    }
    if (lepton[0].pT() < 25 * GeV || lepton[1].pT() < 25 * GeV) break;

    _cutflows->fillnext("2l-ISR");

    // Obtain the 4-momenta of leptons and implement requirements on them
    double n_jets = jets.size();
    if (n_jets < 3 || n_jets > 4) break;
    if (any(jets, hasBTag())) break;

    vector<FourMomentum> jet;
    for (int n = 0; n < n_jets; ++n) {
      jet.push_back(jets[n].momentum());
      M = jet[n].mass();
      jet[n].setPz(0);
      jet[n].setE(sqrt(pow(jet[n].px(), 2) + pow(jet[n].py(), 2) + pow(M, 2)));
    }
    if (jet[0].pT() < 30 * GeV || jet[1].pT() < 30 * GeV) break;

    Vector3 EtMissX = apply<SmearedMET>(event, "MET").vectorPt();
    Vector3 EtMiss = -EtMissX;

    // Obtain the missing transverse momentum vector
    FourMomentum EtMiss1;
    EtMiss1.setPM(EtMiss.x(), EtMiss.y(), EtMiss.z(), sqrt(EtMiss.dot(EtMiss)));

    // Combinatoric Minimization JR
    vector<double> f;
    int indexToReturn = 0;
    int indexValue = 0;
    int newValue = 0;
    if (n_jets == 3) {
      f.push_back((EtMiss1 + jet[1] + jet[2]).p());
      f.push_back((EtMiss1 + jet[0] + jet[2]).p());
      f.push_back((EtMiss1 + jet[0] + jet[1]).p());
      f.push_back((EtMiss1 + jet[0]).p());
      f.push_back((EtMiss1 + jet[1]).p());
      f.push_back((EtMiss1 + jet[2]).p());

      for (int i = 0; i < 6; i++) {
        newValue = f[i];
        if (newValue >= indexValue) {
          indexToReturn = i;
          indexValue = newValue;
        }
      }

      if (indexToReturn == 3 || indexToReturn == 4 || indexToReturn == 5) break;
    }
    else {
      f.push_back((EtMiss1 + jet[0] + jet[1]).p());
      f.push_back((EtMiss1 + jet[0] + jet[2]).p());
      f.push_back((EtMiss1 + jet[0] + jet[3]).p());
      f.push_back((EtMiss1 + jet[1] + jet[2]).p());
      f.push_back((EtMiss1 + jet[1] + jet[3]).p());
      f.push_back((EtMiss1 + jet[2] + jet[3]).p());
      f.push_back((EtMiss1 + jet[0]).p());
      f.push_back((EtMiss1 + jet[1]).p());
      f.push_back((EtMiss1 + jet[2]).p());
      f.push_back((EtMiss1 + jet[3]).p());
      f.push_back((EtMiss1 + jet[1] + jet[2] + jet[3]).p());
      f.push_back((EtMiss1 + jet[0] + jet[2] + jet[3]).p());
      f.push_back((EtMiss1 + jet[0] + jet[1] + jet[3]).p());
      f.push_back((EtMiss1 + jet[0] + jet[1] + jet[2]).p());

      for (int i = 0; i < 14; i++) {
        newValue = f[i];
        if (newValue >= indexValue) {
          indexToReturn = i;
          indexValue = newValue;
        }
      }

      if (indexToReturn == 6 || indexToReturn == 7 || indexToReturn == 8 || indexToReturn == 9
          || indexToReturn == 10 || indexToReturn == 11 || indexToReturn == 12 || indexToReturn == 13)
        break;
    }

    _cutflows->fillnext("2l-ISR");

    // g1 and g2 are the set of jets belonging to the ISR and signal system respectively
    vector<vector<double>> g1, g2;
    if (n_jets == 3) {
      g2 = {{1, 2}, {0, 2}, {0, 1}};
    }
    else {
      g1 = {{2, 3}, {1, 3}, {1, 2}, {0, 3}, {0, 2}, {0, 1}};
      g2 = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
    }

    // m_Z requirement
    double m_Z = ((leptons[0]).mom() + (leptons[1]).mom()).mass();
    if (m_Z < 80 * GeV || m_Z > 100 * GeV) break;
    _cutflows->fillnext("2l-ISR");

    // m_J requirement
    double m_J = ((jets[g2[indexToReturn][0]]).momentum() + (jets[g2[indexToReturn][1]]).momentum())
                     .mass();
    if (m_J < 50 * GeV || m_J > 110 * GeV) break;
    _cutflows->fillnext("2l-ISR");

    // Compute variables delta_phi, R_ISR, P_T_ISR, p_T_I, p_T and implement their requirements
    double p_T_ISR;
    double p_T_I;
    double p_T;
    double R_ISR;
    double delta_phi;
    if (n_jets == 3) {
      FourMomentum CM = EtMiss1 + jet[indexToReturn] + jet[g2[indexToReturn][0]]
          + jet[g2[indexToReturn][1]] + lepton[0] + lepton[1];
      LorentzTransform LT = LorentzTransform::mkFrameTransform(CM);
      p_T_ISR = (LT.transform(jet[indexToReturn])).pT();
      p_T_I = (LT.transform(EtMiss1)).pT();
      p_T = (CM).pT();

      Vector3 p_I = (LT.transform(EtMiss1)).p3();
      Vector3 p_S = (LT.transform(EtMiss1 + jet[g2[indexToReturn][0]] + jet[g2[indexToReturn][1]]
                                  + lepton[0] + lepton[1]))
                        .p3();
      R_ISR = (p_S.dot(p_I)) / (p_S.dot(p_S));
      delta_phi = deltaPhi((LT.transform(EtMiss1)).p3(), (LT.transform(jet[indexToReturn])).p3());
    }
    else {

      FourMomentum CM = EtMiss1 + jet[g1[indexToReturn][0]] + jet[g1[indexToReturn][1]]
          + jet[g2[indexToReturn][0]] + jet[g2[indexToReturn][1]] + lepton[0] + lepton[1];
      LorentzTransform LT = LorentzTransform::mkFrameTransform(CM);
      p_T_ISR = (LT.transform(jet[g1[indexToReturn][0]] + jet[g1[indexToReturn][1]])).pT();
      p_T_I = (LT.transform(EtMiss1)).pT();
      p_T = (CM).pT();

      Vector3 p_I = (LT.transform(EtMiss1)).p3();
      Vector3 p_S = (LT.transform(EtMiss1 + jet[g2[indexToReturn][0]] + jet[g2[indexToReturn][1]]
                                  + lepton[0] + lepton[1]))
                        .p3();
      R_ISR = (p_S.dot(p_I)) / (p_S.dot(p_S));
      delta_phi = deltaPhi((LT.transform(EtMiss1)).p3(),
                           (LT.transform(jet[g1[indexToReturn][0]] + jet[g1[indexToReturn][1]])).p3());
    }

    if (delta_phi < 2.8) break;
    _cutflows->fillnext("2l-ISR");

    if (R_ISR < 0.4 || R_ISR > 0.75) break;
    _cutflows->fillnext("2l-ISR");

    if (p_T_ISR < 180 * GeV) break;
    _cutflows->fillnext("2l-ISR");

    if (p_T_I < 100 * GeV) break;
    _cutflows->fillnext("2l-ISR");

    if (p_T > 20 * GeV) break;
    _cutflows->fillnext("2l-ISR");

    break;
  }


  // 3-lepton High (n=0), Int (n=1) and Low (n=2) Selection
  for (int n = 0; n < 3; ++n) {

    while (true) {

      // Require the leading electron and the leading muon to have pT > 25 GeV
      if (elecs.size() != 0 && elecs[0].pT() < 25 * GeV) break;
      if (muons.size() != 0 && muons[0].pT() < 25 * GeV) break;

      // Require 3 leptons and a pair of leptons with opposite charge and same flavour
      double n_leptons = leptons.size();
      if (n_leptons != 3) break;

      vector<vector<double>> g = {{1, 2}, {0, 2}, {0, 1}};
      int indexToReturn = 3;
      int indexValue = 100000;
      int newValue = 0;
      for (int i = 0; i < 3; i++) {
        if (!(leptons[g[i][0]].abspid() == leptons[g[i][1]].abspid()
              && leptons[g[i][0]].charge() != leptons[g[i][1]].charge()))
          continue;
        newValue = abs((leptons[g[i][0]].mom() + leptons[g[i][1]].mom()).mass() - 90 * GeV);
        if (newValue <= indexValue) {
          indexToReturn = i;
          indexValue = newValue;
        }
      }

      if (indexToReturn == 3) break;

      _cutflows->fillnext("3l"s + _types[n]);

      // Requirements on n_jets
      double n_jets = jets.size();
      if (n != 2) {
        if (n_jets >= 3) break;
        if (any(jets, hasBTag())) break;
      }
      else {
        if (n_jets != 0) break;
      }

      // Obtain 4-momentum of the leptons and implement the requirements on their transverse momentum
      vector<FourMomentum> lepton;
      for (int l = 0; l < 3; ++l) {
        lepton.push_back(leptons[l].mom());
      }

      double p_l1_T = lepton[0].pT();
      if (p_l1_T < 60) break;

      if (n == 0) {
        double p_l2_T = lepton[1].pT();
        if (p_l2_T < 60 * GeV) break;
      }
      else if (n == 1) {
        double p_l2_T = lepton[1].pT();
        if (p_l2_T < 50 * GeV) break;
      }
      else {
        double p_l2_T = lepton[1].pT();
        if (p_l2_T < 40 * GeV) break;
      }

      if (n == 0) {
        double p_l3_T = (lepton[2]).pT();
        if (p_l3_T < 40 * GeV) break;
      }
      else {
        double p_l3_T = (lepton[2]).pT();
        if (p_l3_T < 30 * GeV) break;
      }

      // Pre-selection Cut
      _cutflows->fillnext("3l"s + _types[n]);

      // Requirement on m_ll
      double m_ll = (lepton[g[indexToReturn][0]] + lepton[g[indexToReturn][1]]).mass();
      if (m_ll < 75 * GeV || m_ll > 105 * GeV) break;

      _cutflows->fillnext("3l"s + _types[n]);

      // Obtain the missing transverse momentum vector
      Vector3 EtMissX = apply<SmearedMET>(event, "MET").vectorPt();
      Vector3 EtMiss = -EtMissX;

      // Requirement on m_W_T
      double deltaphi = deltaPhi(EtMiss, (lepton[indexToReturn]).p3());
      double m_W_T = sqrt(2 * ((lepton[indexToReturn]).pT()) * sqrt(EtMiss.dot(EtMiss))
                          * (1 - cos(deltaphi)));

      if (n == 0) {
        if (m_W_T < 150 * GeV) break;
      }
      else if (n == 1) {
        if (m_W_T < 130 * GeV) break;
      }
      else {
        if (m_W_T < 100 * GeV) break;
      }

      _cutflows->fillnext("3l"s + _types[n]);

      // Invisible mass JR and Invisible Rapidity JR to obtain Invisible System 4-momentum
      FourMomentum P_V = lepton[0] + lepton[1] + lepton[2];
      double M_I = sqrt(P_V.mass2() - 4 * m_ll * ((lepton[indexToReturn]).mass()));
      double M_V = P_V.mass();
      double p_I_z = (P_V.pz()) * sqrt(EtMiss.dot(EtMiss) + sqr(M_I)) / sqrt(P_V.pT2() + sqr(M_V));

      FourMomentum P_I;
      P_I.setPM(EtMiss.x(), EtMiss.y(), p_I_z, M_I);

      // Lorentz boost to CM frame
      LorentzTransform LT = LorentzTransform::mkFrameTransform(P_I + P_V);
      FourMomentum P_F_I;   //4-momentum of invisible system in CM frame
      FourMomentum P_F_Va;  //4-momentum of visible system a in CM frame
      FourMomentum P_F_Vb;  //4-momentum of visible system b in CM frame
      FourMomentum P_F_V;   //4-momentum of visible system in CM frame
      FourMomentum P_F_Va1; //4-momentum of visible system a1 in CM frame
      FourMomentum P_F_Va2; //4-momentum of visible system a2 in CM frame
      FourMomentum P_F_Vb1; //4-momentum of visible system b1 in CM frame
      FourMomentum P_F_Vb2; //4-momentum of visible system b2 in CM frame
      if ((lepton[indexToReturn]).mass()
          > (lepton[g[indexToReturn][0]] + lepton[g[indexToReturn][1]]).mass()) {
        P_F_I = LT.transform(P_I);
        P_F_Va = LT.transform(lepton[indexToReturn]);
        P_F_Vb = LT.transform(lepton[g[indexToReturn][0]] + lepton[g[indexToReturn][1]]);
        P_F_V = LT.transform(P_V);
        //
        P_F_Va1 = LT.transform(lepton[indexToReturn]);
        P_F_Vb1 = LT.transform(lepton[g[indexToReturn][0]]);
        P_F_Vb2 = LT.transform(lepton[g[indexToReturn][1]]);
      }
      else {

        P_F_I = LT.transform(P_I);
        P_F_Va = LT.transform(lepton[g[indexToReturn][0]] + lepton[g[indexToReturn][1]]);
        P_F_Vb = LT.transform(lepton[indexToReturn]);
        P_F_V = LT.transform(P_V);
        //
        P_F_Va1 = LT.transform(lepton[g[indexToReturn][0]]);
        P_F_Va2 = LT.transform(lepton[g[indexToReturn][1]]);
        P_F_Vb1 = LT.transform(lepton[indexToReturn]);
      }

      // Obtain variables defined in the Contra-boost Invariant JR
      double M2c = 2 * ((P_F_Va.E()) * (P_F_Vb.E()) + (P_F_Va.p3()).dot(P_F_Vb.p3()));
      double m2Va = sqr(P_F_Va.mass());
      double m2Vb = sqr(P_F_Vb.mass());
      double ka = m2Va - m2Vb + M2c - 2 * sqrt(m2Va) * sqrt(m2Vb);
      double kb = m2Vb - m2Va + M2c - 2 * sqrt(m2Va) * sqrt(m2Vb);
      double kn = ka * m2Va - kb * m2Vb + M2c * (kb - ka) / 2
          + (0.5) * sqrt(pow(ka + kb, 2) * (pow(M2c, 2) - 4 * m2Va * m2Vb));
      double k2d = sqr(ka) * m2Va + sqr(kb) * m2Vb + ka * kb * M2c;
      double k = kn / k2d;
      double ca = (1 + k * ka) / 2;
      double cb = (1 + k * kb) / 2;
      double c = 0.5 * (P_F_V.E() + sqrt(pow(P_F_V.E(), 2) + pow(M_I, 2) - pow(P_V.mass(), 2)))
          / (ca * (P_F_Va.E()) + cb * (P_F_Vb.E()));

      // Apply Contra-boost Invariant JR to obtain invisible particles' 4-momenta
      double p_F_Iax = (P_F_Va.px()) * (c * ca - 1) - (P_F_Vb.px()) * c * cb;
      double p_F_Iay = (P_F_Va.py()) * (c * ca - 1) - (P_F_Vb.py()) * c * cb;
      double p_F_Iaz = (P_F_Va.pz()) * (c * ca - 1) - (P_F_Vb.pz()) * c * cb;
      double p_F_Ibx = (P_F_Vb.px()) * (c * cb - 1) - (P_F_Va.px()) * c * ca;
      double p_F_Iby = (P_F_Vb.py()) * (c * cb - 1) - (P_F_Va.py()) * c * ca;
      double p_F_Ibz = (P_F_Vb.pz()) * (c * cb - 1) - (P_F_Va.pz()) * c * ca;
      double E_F_Ia = (c * ca - 1) * (P_F_Va.E()) + c * cb * (P_F_Vb.E());
      double E_F_Ib = (c * cb - 1) * (P_F_Vb.E()) + c * ca * (P_F_Va.E());

      FourMomentum P_F_Ia;
      FourMomentum P_F_Ib;
      P_F_Ia.setPE(p_F_Iax, p_F_Iay, p_F_Iaz, E_F_Ia);
      P_F_Ib.setPE(p_F_Ibx, p_F_Iby, p_F_Ibz, E_F_Ib);

      // Lorentz Transform to the Pb frame from the CM frame
      LorentzTransform LTP;
      if ((lepton[indexToReturn]).mass()
          > (lepton[g[indexToReturn][0]] + lepton[g[indexToReturn][1]]).mass()) {
        LTP = LorentzTransform::mkFrameTransform(P_F_Vb + P_F_Ib);
      }
      else {
        LTP = LorentzTransform::mkFrameTransform(P_F_Va + P_F_Ia);
      }

      // p_lab_T_PP/(p_lab_T_PP+p_lab_T_31) requirement
      LorentzTransform LTR = LorentzTransform::mkObjTransform(P_I + P_V);
      double p_lab_T_PP = (LTR.transform(P_F_Va + P_F_Vb + P_F_Ia + P_F_Ib)).pT();
      double H_PP_T_31;
      double H_PP_31;
      double H_Pb_11;
      double H_Pb_21;
      if ((lepton[indexToReturn]).mass()
          > (lepton[g[indexToReturn][0]] + lepton[g[indexToReturn][1]]).mass()) {
        H_PP_T_31 = P_F_Va1.pT() + P_F_Vb1.pT() + P_F_Vb2.pT() + (P_F_Ia + P_F_Ib).pT();
        H_PP_31 = P_F_Va1.p() + P_F_Vb1.p() + P_F_Vb2.p() + (P_F_Ia + P_F_Ib).p();
        H_Pb_11 = (LTP.transform(P_F_Vb1 + P_F_Vb2)).p() + (LTP.transform(P_F_Ib)).p();
        H_Pb_21 = (LTP.transform(P_F_Vb1)).p() + (LTP.transform(P_F_Vb2)).p()
            + (LTP.transform(P_F_Ib)).p();
      }
      else {
        H_PP_T_31 = P_F_Va1.pT() + P_F_Va2.pT() + P_F_Vb1.pT() + (P_F_Ia + P_F_Ib).pT();
        H_PP_31 = P_F_Va1.p() + P_F_Va2.p() + P_F_Vb1.p() + (P_F_Ia + P_F_Ib).p();
        H_Pb_11 = (LTP.transform(P_F_Va1 + P_F_Va2)).p() + (LTP.transform(P_F_Ia)).p();
        H_Pb_21 = (LTP.transform(P_F_Va1)).p() + (LTP.transform(P_F_Va2)).p()
            + (LTP.transform(P_F_Ia)).p();
      }

      double fraction1 = H_PP_T_31 / H_PP_31;
      if (n == 0) {
        if (fraction1 < 0.75) break;
      }
      else if (n == 1) {
        if (fraction1 < 0.8) break;
      }
      else {
        if (fraction1 < 0.9) break;
      }
      _cutflows->fillnext("3l"s + _types[n]);

      double fraction2 = H_Pb_11 / H_Pb_21;
      if (n == 0) {
        if (fraction2 < 0.8) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }
      else if (n == 1) {
        if (fraction2 < 0.75) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }
      else { /* ??? */
      }

      if (n == 0) {
        if (H_PP_31 < 550 * GeV) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }
      else if (n == 1) {
        if (H_PP_31 < 450 * GeV) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }
      else {
        if (H_PP_31 < 250 * GeV) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }

      double fraction3 = p_lab_T_PP / (p_lab_T_PP + H_PP_T_31);
      if (n == 0) {
        if (fraction3 > 0.2) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }
      else if (n == 1) {
        if (fraction3 > 0.15) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }
      else {
        if (fraction3 > 0.05) break;
        _cutflows->fillnext("3l"s + _types[n]);
      }

      break;
    }
  }


  // 3-lepton ISR Selection
  while (true) {

    // Require the leading electron and the leading muon to have pT > 25GeV
    if (elecs.size() != 0 && elecs[0].pT() < 25 * GeV) break;
    if (muons.size() != 0 && muons[0].pT() < 25 * GeV) break;

    // Require only 3 leptons
    double n_leptons = leptons.size();
    if (n_leptons != 3) break;

    // Require and identify the pair of opposite-charged and same-flavoured leptons
    vector<vector<double>> g = {{1, 2}, {0, 2}, {0, 1}};
    int indexToReturn = 3;
    int indexValue = 100000;
    int newValue = 0;
    for (int i = 0; i < 3; i++) {
      if (!(leptons[g[i][0]].abspid() == leptons[g[i][1]].abspid()
            && leptons[g[i][0]].charge() != leptons[g[i][1]].charge()))
        continue;
      newValue = abs(((leptons[g[i][0]]).mom() + (leptons[g[i][1]]).mom()).mass() - 90);
      if (newValue <= indexValue) {
        indexToReturn = i;
        indexValue = newValue;
      }
    }

    if (indexToReturn == 3) break;
    _cutflows->fillnext("3l-ISR");

    // Requirements on jet number and forbid B-tags
    double n_jets = jets.size();
    if (n_jets < 1 || n_jets > 3) break;
    if (any(jets, hasBTag())) break;

    // Obtain the 4-momenta of leptons and implement requirements on them
    vector<FourMomentum> lepton;
    double M;
    for (int n = 0; n < 3; ++n) {
      lepton.push_back(leptons[n].mom());
      M = lepton[n].mass();
      lepton[n].setPz(0);
      lepton[n].setE(sqrt(pow(lepton[n].px(), 2) + pow(lepton[n].py(), 2) + pow(M, 2)));
    }
    if (lepton[0].pT() < 25 * GeV || lepton[1].pT() < 25 * GeV || lepton[2].pT() < 20 * GeV) break;

    // Pre-selection Cut
    _cutflows->fillnext("3l-ISR");

    // Obtain the missing momentum 3-vector
    Vector3 EtMiss = apply<SmearedMET>(event, "MET").vectorMissingPt();

    // Requirement on m_ll
    double m_ll = (leptons[g[indexToReturn][0]].mom() + leptons[g[indexToReturn][1]].mom()).mass();
    if (m_ll < 75 * GeV || m_ll > 105 * GeV) break;
    _cutflows->fillnext("3l-ISR");

    // Requirement on m_W_T
    double deltaphi = deltaPhi(EtMiss, (lepton[indexToReturn]).p3());
    double m_W_T = sqrt(2 * ((lepton[indexToReturn]).pT()) * sqrt(EtMiss.dot(EtMiss))
                        * (1 - cos(deltaphi)));
    if (m_W_T < 100 * GeV) break;
    _cutflows->fillnext("3l-ISR");

    // Obtain the missing transverse 4-momentum
    FourMomentum EtMiss1;
    EtMiss1.setPM(EtMiss.x(), EtMiss.y(), EtMiss.z(), sqrt(EtMiss.dot(EtMiss)));
    double p_T_ISR;
    double p_T_I;
    double p_T;
    double R_ISR;
    double delta_phi;
    FourMomentum ISR;

    // Obtain the 4-momentum of the ISR system
    vector<FourMomentum> jet;
    for (int n = 0; n < n_jets; ++n) {
      jet.push_back(jets[n].momentum());
      M = jet[n].mass();
      jet[n].setPz(0);
      jet[n].setE(sqrt(pow(jet[n].px(), 2) + pow(jet[n].py(), 2) + pow(M, 2)));
    }

    if (n_jets == 1) {
      ISR = jet[0];
    }
    else if (n_jets == 2) {
      ISR = jet[0] + jet[1];
    }
    else {
      ISR = jet[0] + jet[1] + jet[2];
    }

    // Four Momentum of the CM System
    FourMomentum CM = EtMiss1 + ISR + lepton[0] + lepton[1] + lepton[2];
    LorentzTransform LT = LorentzTransform::mkFrameTransform(CM);
    p_T_ISR = (LT.transform(ISR)).pT();
    p_T_I = (LT.transform(EtMiss1)).pT();
    p_T = (CM).pT();

    Vector3 p_I = (LT.transform(EtMiss1)).p3();
    Vector3 p_S = (LT.transform(EtMiss1 + lepton[0] + lepton[1] + lepton[2])).p3();
    R_ISR = (p_S.dot(p_I)) / (p_S.dot(p_S));
    delta_phi = angle((LT.transform(EtMiss1)).p3(), (LT.transform(ISR)).p3());

    if (delta_phi < 2.0) break;
    _cutflows->fillnext("3l-ISR");

    if (R_ISR < 0.55 || R_ISR > 1.0) break;
    _cutflows->fillnext("3l-ISR");

    if (p_T_ISR < 100 * GeV) break;
    _cutflows->fillnext("3l-ISR");

    if (p_T_I < 80 * GeV) break;
    _cutflows->fillnext("3l-ISR");

    if (p_T > 25 * GeV) break;
    _cutflows->fillnext("3l-ISR");

    break;
  }
}


/// Finalise cutflow scaling etc.
void finalize() {

  vector<double> scales{1673., 4369., 65247};
  for (size_t n = 0; n < _types.size(); ++n) {
    _cutflows->binAt("2l"s + _types[n])->normalizeFirst(scales[n]);
    _cutflows->binAt("3l"s + _types[n])->normalizeFirst(scales[n]);
  }
  _cutflows->binAt("2l-ISR")->normalizeFirst(scales[2]);
  _cutflows->binAt("3l-ISR")->normalizeFirst(scales[2]);

  MSG_INFO("CUTFLOWS:\n\n" << _cutflows);
}

CutflowsPtr _cutflows;
const vector<string> _types{"-high", "-int", "-low"};

};

RIVET_DECLARE_PLUGIN(ATLAS_2018_I1676551);

} ```