Rivet analyses
Measurement of electroweak 4 lepton + 2 jet production
Experiment: ATLAS (LHC)
Inspire ID: 2690799
Status: VALIDATED
Authors: - Zuchen Huang
References: - Expt page: ATLAS-STDM-2020-02 - JHEP 01 (2024) 004 - arXiv: 2308.12324
Beams: p+ p+
Beam energies: (6500.0, 6500.0)GeV
Run details: - inclusive 4 leptons + 2jet production at 13 TeV
Differential cross-sections are measured for the production of four charged leptons in association with two jets. These measurements are sensitive to final states in which the jets are produced via the strong interaction as well as to the purely-electroweak vector boson scattering process. The analysis is performed using proton-proton collision data collected by ATLAS at $\sqrt{s}$=13 TeV and with an integrated luminosity of 140 fb−1. The data are corrected for the effects of detector inefficiency and resolution and are compared to state-of-the-art Monte Carlo event generator predictions. The differential cross-sections are used to search for anomalous weak-boson self-interactions that are induced by dimension-six and dimension-eight operators in Standard Model effective field theory.
Source
code:ATLAS_2023_I2690799.cc
// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Projections/VetoedFinalState.hh"
#include "Rivet/Projections/FastJets.hh"
#include "Rivet/Projections/LeptonFinder.hh"
#include "Rivet/Projections/PromptFinalState.hh"
namespace Rivet {
/// @brief Measurement of electroweak 4 lepton + 2 jet production
class ATLAS_2023_I2690799 : public Analysis {
public:
/// Constructor
RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2023_I2690799);
/// A Z Boson structrue:
struct Z_can {
int lep_1, lep_2;
double dZ;
FourMomentum p;
};
/// Book histograms and initialise projections before the run
void init() {
// Initialise and register projections
// FinalState of prompt photons and bare muons and electrons in the event
PromptFinalState photons(Cuts::abspid == PID::PHOTON);
PromptFinalState electrons(Cuts::abspid == PID::ELECTRON);
PromptFinalState muons(Cuts::abspid == PID::MUON);
// Dress the prompt bare leptons with prompt photons within dR < 0.05,
// and apply some fiducial cuts on the dressed leptons
Cut cuts_el = (Cuts::pT > 7*GeV) && ( Cuts::abseta < 2.47);
Cut cuts_mu = (Cuts::pT > 5*GeV) && ( Cuts::abseta < 2.7);
LeptonFinder dressed_electrons(electrons, photons, 0.1, cuts_el);
declare(dressed_electrons, "DressedElectrons");
LeptonFinder dressed_muons(muons, photons, 0.1, cuts_mu);
declare(dressed_muons, "DressedMuons");
// We define AntiKt4TruthWZJets here, the leptons here are not used for next step selection.
// Muons
PromptFinalState bare_mu(Cuts::abspid == PID::MUON, TauDecaysAs::PROMPT); // true = use muons from prompt tau decays
LeptonFinder all_dressed_mu(bare_mu, photons, 0.1, Cuts::abseta < 2.5);
//
// Electrons
PromptFinalState bare_el(Cuts::abspid == PID::ELECTRON, TauDecaysAs::PROMPT); // true = use electrons from prompt tau decays
LeptonFinder all_dressed_el(bare_el, photons, 0.1, Cuts::abseta < 2.5);
VetoedFinalState vfs(FinalState(Cuts::abseta < 4.5));
// The final-state particles declared above are clustered using FastJet with
// the anti-kT algorithm and a jet-radius parameter 0.4
// AntiKt4TruthWZJets
vfs.addVetoOnThisFinalState(all_dressed_el);
vfs.addVetoOnThisFinalState(all_dressed_mu);
FastJets jetfs(vfs, JetAlg::ANTIKT, 0.4, JetMuons::ALL, JetInvisibles::DECAY);
declare(jetfs, "jets");
// Book histograms
book( _h["m_jj_SR"], 7, 1, 1);
book( _h["m_jj_CR"], 8, 1, 1);
book( _h["m_4l_SR"], 9, 1, 1);
book( _h["m_4l_CR"], 10, 1, 1);
book( _h["pt_4l_SR"], 11, 1, 1);
book( _h["pt_4l_CR"], 12, 1, 1);
book( _h["dphi_SR"], 13, 1, 1);
book( _h["dphi_CR"], 14, 1, 1);
book( _h["dY_SR"], 15, 1, 1);
book( _h["dY_CR"], 16, 1, 1);
book( _h["costs1_SR"], 17, 1, 1);
book( _h["costs1_CR"], 18, 1, 1);
book( _h["costs3_SR"], 19, 1, 1);
book( _h["costs3_CR"], 20, 1, 1);
book( _h["pt_jj_SR"], 21, 1, 1);
book( _h["pt_jj_CR"], 22, 1, 1);
book( _h["pt_4ljj_SR"], 23, 1, 1);
book( _h["pt_4ljj_CR"], 24, 1, 1);
book( _h["st_4ljj_SR"], 25, 1, 1);
book( _h["st_4ljj_CR"], 26, 1, 1);
}
/// Perform the per-event analysis
void analyze(const Event& event) {
// Retrieve dressed leptons, sorted by pT
DressedLeptons muons = apply<LeptonFinder>(event, "DressedMuons").dressedLeptons();
DressedLeptons elecs = apply<LeptonFinder>(event, "DressedElectrons").dressedLeptons();
DressedLeptons leps;
leps.insert( leps.end(), muons.begin(), muons.end() );
leps.insert( leps.end(), elecs.begin(), elecs.end() );
size_t n_leps = leps.size();
if (n_leps < 4) vetoEvent;
//leading and sub-leading leps pt
sort(leps.begin(), leps.end(), cmpMomByPt);
if (leps[0].pT() < 20*GeV || leps[1].pT() < 20*GeV) vetoEvent;
//lepton seperation
double dR_min = 9999;
for (size_t i = 0; i < n_leps - 1; ++i) {
for (size_t j = i + 1; j < n_leps; ++j) {
double dR = deltaR(leps[i], leps[j]);
if (dR < dR_min) dR_min = dR;
}
}
if (dR_min < 0.05) vetoEvent;
//ZZ pairing
size_t sfos_pair = 0;
double minm2l = 9999.;
vector<Z_can> z;
Particle *l1, *l3;
for (size_t i = 0; i < n_leps - 1; ++i) {
for (size_t j = i + 1; j < n_leps; ++j) {
if (leps[i].pid() != -1*leps[j].pid()) continue;
++sfos_pair;
FourMomentum twoL = leps[i].momentum() + leps[j].momentum();
if (twoL.mass() < minm2l) minm2l = twoL.mass();
double deltaM = fabs( twoL.mass() - Zmass_PDG);
Z_can tmp_z;
tmp_z.dZ = deltaM;
tmp_z.p = twoL;
if (leps[i].pid() < 0) {
tmp_z.lep_1 = i;
tmp_z.lep_2 = j;
} else{
tmp_z.lep_1 = j;
tmp_z.lep_2 = i;
}
z.push_back(tmp_z);
}
}
if (minm2l < 5*GeV) vetoEvent; //All SFOS ll pairs should have m2l > 5 GeV.
if (sfos_pair < 2) vetoEvent;
std::sort(z.begin(), z.end(), [](const Z_can& z1, const Z_can& z2) { return z1.dZ < z2.dZ;});
int z1_id = -1;
int z2_id = -1;
FourMomentum fourL;
bool found_4l = false;
for (size_t i = 0; i < z.size() - 1; ++i) {
for (size_t j = i + 1; j < z.size(); ++j) {
if (z[i].lep_1 == z[j].lep_1 ||
z[i].lep_1 == z[j].lep_2 ||
z[i].lep_2 == z[j].lep_1 ||
z[i].lep_2 == z[j].lep_2) continue;
fourL = z[i].p + z[j].p;
if (fourL.mass() > 130*GeV) {
z1_id = i;
z2_id = j;
found_4l =true;
break;
}
}
if (found_4l) break;
}
if (!found_4l) vetoEvent;
// To judge which is the leading pair
FourMomentum twoL_1, twoL_2;
if (abs(z[z1_id].p.rap()) > abs(z[z2_id].p.rap())){
twoL_1 = z[z1_id].p;
twoL_2 = z[z2_id].p;
int i = z[z1_id].lep_1;
int j = z[z2_id].lep_1;
l1 = &leps[i];
l3 = &leps[j];
} else {
twoL_1 = z[z2_id].p;
twoL_2 = z[z1_id].p;
int i = z[z2_id].lep_1;
int j = z[z1_id].lep_1;
l1 = &leps[i];
l3 = &leps[j];
}
// Retrieve clustered jets, sorted by pT, with a minimum pT cut
Jets jets = apply<FastJets>(event, "jets").jetsByPt(Cuts::pT > 30*GeV && Cuts::absrap < 4.5);
if (jets.size() < 2) vetoEvent;
if (jets[0].pt() < 40*GeV) vetoEvent;
FourMomentum twoJ;
double dY = -1;
int jet_id1 = -1, jet_id2 = -1;
bool found_2j = false;
for (size_t i = 0; i < jets.size() - 1; ++i) {
for (size_t j = i + 1; j < jets.size(); ++j) {
if (jets[i].rap()*jets[j].rap() > 0) continue; // First pair in A/C
dY = fabs(jets[i].rap() - jets[j].rap());
twoJ = jets[i].momentum() + jets[j].momentum();
jet_id1 = i;
jet_id2 = j;
found_2j = true;
break;
}
if (found_2j) break;
}
if (!found_2j) vetoEvent;
if (dY < 2) vetoEvent;
if (twoJ.mass() < 300*GeV) vetoEvent;
// Calculate Observables!
double phi1, phi2;
if (jets[jet_id1].rap() > jets[jet_id2].rap()) {
phi1 = jets[jet_id1].phi(); phi2 = jets[jet_id2].phi();
}
else {
phi1 = jets[jet_id2].phi(); phi2 = jets[jet_id1].phi();
}
const double dphi = mapAngleMPiToPi(phi1 - phi2);
const double mjj = twoJ.mass();
const double m4l = fourL.mass();
const double pt4l = fourL.pT();
const double ptjj = twoJ.pT();
const double pt4ljj = (fourL + twoJ).pT();
const double st4ljj = twoL_1.pT() + twoL_2.pT() + jets[jet_id1].pT() + jets[jet_id2].pT();
const double centrality = abs(fourL.rap() - 0.5*(jets[jet_id1].rap() + jets[jet_id2].rap())) \
/ abs(jets[jet_id1].rap() - jets[jet_id2].rap());
//negetive leps1 in Z rest frame work
Vector3 beta2lcom_1 = twoL_1.betaVec();
LorentzTransform com2lboost_1 = LorentzTransform::mkFrameTransformFromBeta(beta2lcom_1);
FourMomentum l1_2lcom = com2lboost_1.transform(l1->momentum());
const double costs1 = cos(twoL_1.angle(l1_2lcom));
//negetive leps3 in Z rest frame work
Vector3 beta2lcom_2 = twoL_2.betaVec();
LorentzTransform com2lboost_2 = LorentzTransform::mkFrameTransformFromBeta(beta2lcom_2);
FourMomentum l3_2lcom = com2lboost_2.transform(l3->momentum());
const double costs3 = cos(twoL_2.angle(l3_2lcom));
const string suff = (centrality < 0.4)? "_SR" : "_CR";
_h["dphi"+suff]->fill(dphi);
_h["m_jj"+suff]->fill(mjj/GeV);
_h["dY"+suff]->fill(dY);
_h["m_4l"+suff]->fill(m4l/GeV);
_h["pt_4l"+suff]->fill(pt4l/GeV);
_h["pt_jj"+suff]->fill(ptjj/GeV);
_h["pt_4ljj"+suff]->fill(pt4ljj/GeV);
_h["st_4ljj"+suff]->fill(st4ljj/GeV);
_h["costs1"+suff]->fill(costs1/GeV);
_h["costs3"+suff]->fill(costs3/GeV);
}
/// Normalise histograms etc., after the run
void finalize() {
const double norm = crossSection()/sumOfWeights()/femtobarn;
scale(_h, norm);
}
private:
map<string, Histo1DPtr> _h;
const double Zmass_PDG = 91.1876;
};
RIVET_DECLARE_PLUGIN(ATLAS_2023_I2690799);
}