Rivet analyses
ZZ -> 4 leptons / 2 leptons and 2 neutrinos measurment at 8TeV
Experiment: ATLAS (LHC)
Inspire ID: 1494075
Status: VALIDATED
Authors: - Luzhan Yue - Peng Wang - Jon Butterworth - Chris Gutschow
References: - Expt page: ATLAS-STDM-2014-16 - JHEP 01 (2017) 099 - 10.1007/JHEP01(2017)099 - arXiv: 1610.07585
Beams: p+ p+
Beam energies: (4000.0, 4000.0)GeV
Run details: - pp -> ZZ -> 4L or 2L2nu measurement at 8TeV.
A measurement of the ZZ production cross section in the llll and llnunu (l = e , mu) in the proton-proton collisions at 8 TeV centre-of-mass energy at the LHC at CERN. Using data corresponding to an integrated luminosity of 20.3/fb collected by the ATLAS experiment in 2012.
Source
code:ATLAS_2016_I1494075.cc
// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Projections/FastJets.hh"
#include "Rivet/Projections/LeptonFinder.hh"
#include "Rivet/Projections/DirectFinalState.hh"
#include "Rivet/Projections/PromptFinalState.hh"
#include "Rivet/Projections/VetoedFinalState.hh"
#include "Rivet/Projections/InvisibleFinalState.hh"
namespace Rivet {
/// @brief ATLAS ZZ --> 4l and 2l2v
class ATLAS_2016_I1494075 : public Analysis {
public:
/// Constructor
//
RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2016_I1494075);
/// @name Analysis methods
/// @{
/// Book histograms and initialise projections before the run
void init() {
_mode = 0;
if (getOption("LMODE") == "2L2NU") _mode = 2;
if (getOption("LMODE") == "4L") _mode = 1;
PromptFinalState prompt_photons(Cuts::abspid == PID::PHOTON);
PromptFinalState prompt_ele(Cuts::abspid == PID::ELECTRON);
PromptFinalState prompt_mu(Cuts::abspid == PID::MUON);
// Wide lepton cuts which cover both channels and are used for the jet veto.
Cut dressedele_cuts = (Cuts::abseta < 4.9) && (Cuts::pT > 7*GeV);
Cut dressedmu_cuts = (Cuts::abseta < 2.7) && (Cuts::pT > 7*GeV);
const LeptonFinder dressedelectrons(prompt_ele, prompt_photons, 0.1, dressedele_cuts);
const LeptonFinder dressedmuons(prompt_mu, prompt_photons, 0.1, dressedmu_cuts);
declare(dressedelectrons, "electrons");
declare(dressedmuons, "muons");
VisibleFinalState vfs;
VetoedFinalState jetinput(vfs);
jetinput.addVetoOnThisFinalState(dressedmuons);
if (_mode != 1) declare(InvisibleFinalState(OnlyPrompt::YES), "MET");
FastJets fastjets(jetinput, JetAlg::ANTIKT, 0.4);
declare (fastjets, "Jets");
// ZZ to four leptons channel
book(_h["leading_ll_pt"], 2, 1, 1);
book(_h["Njets"], 3, 1, 1);
book(_h["leading_ll_phi"], 4, 1, 1);
book(_h["ZZ_rapidity"], 5, 1, 1);
// ZZ to lvlv channel
book(_h["dilepton_pt"], 6, 1, 1);
book(_h["llphi_lvchannel"], 7, 1, 1);
book(_h["mzz_lvchannel"], 8, 1, 1);
}
struct Zstate : public ParticlePair {
Zstate() { }
Zstate(ParticlePair _particlepair) : ParticlePair(_particlepair) { }
FourMomentum mom() const { return first.momentum() + second.momentum();}
double rapid() const { return ((first.momentum() + second.momentum()).rapidity()); }
double dphi() const { return deltaPhi(first.ptvec(), second.ptvec()); }
};
/// Perform the per-event analysis
void analyze(const Event& event) {
// find out how many good jets
//Jets jets = apply<FastJets>(event, "Jets").jetsByPt();
Particles cand_e = apply<LeptonFinder>(event, "electrons").particlesByPt();
Particles cand_mu = apply<LeptonFinder>(event, "muons").particlesByPt();
Jets jets = apply<FastJets>(event, "Jets").jetsByPt(Cuts::abseta < 4.5 && Cuts::pT>25*GeV);
idiscardIfAnyDeltaRLess(jets, cand_e, 0.3);
//llvv channel
if (_mode != 1) {
// jet veto
if (jets.empty()){
Particles selected_pair;
Vector3 met_vec;
const FinalState& metfs = apply<InvisibleFinalState>(event, "MET");
for (const Particle& p : metfs.particles()) met_vec += p.mom().perpVec();
for ( const Particle& mu : cand_mu ) {
if (mu.pT() > 25*GeV && mu.abseta() < 2.5 ) {
selected_pair.push_back(mu);
}
}
for ( const Particle& e : cand_e ) {
if (e.pT() > 25*GeV && e.abseta() < 2.5 ) {
selected_pair.push_back(e);
}
}
// selections on pair.
if ((selected_pair.size() == 2) // exactly two leptons
&& (selected_pair[0].abspid() == selected_pair[1].abspid()) //same flavour
&& (deltaR(selected_pair[0],selected_pair[1]) > 0.3) // deltaR > 0.3
&& (selected_pair[0].pid() * selected_pair[1].pid() < 0)) { // opposite sign
//ensure the first one is leading lepton
if (selected_pair[0].momentum().pT() < selected_pair[1].momentum().pT()) {
std::swap(selected_pair[0], selected_pair[1]);
}
//ZZ four momentum, three momentum if from invisble final states
const FourMomentum Z_1_mom = selected_pair[0].momentum() + selected_pair[1].momentum();
const double axial_Etmiss = -1.0*met_vec.mod()*cos(deltaPhi(met_vec, Z_1_mom.ptvec()));
double pT_balance = fabs( (met_vec.mod() - Z_1_mom.pT()) /Z_1_mom.pT() );
if (axial_Etmiss > 90*GeV && pT_balance < 0.4 && inRange(Z_1_mom.mass(), 76*GeV, 106*GeV) ) {
double mz_pdg2 = 91.1876*91.1876*GeV*GeV;
// transverse mass
double mTrans = sqrt(
sqr((sqrt(Z_1_mom.pT()*Z_1_mom.pT() + mz_pdg2) + sqrt(met_vec.mod2() + mz_pdg2)))
- (Z_1_mom.ptvec() + met_vec.perpVec()).mod2()
);
_h["mzz_lvchannel"]->fill(mTrans/GeV);
_h["llphi_lvchannel"]->fill(deltaPhi(selected_pair[0].momentum().ptvec(), selected_pair[1].momentum().ptvec()));
_h["dilepton_pt"]->fill(Z_1_mom.pT()/GeV);
}
}
}
}
//for llll
if (_mode != 2) {
///////////
// Insert selected muons then electrons into the lepton 4l final state
///////////
DressedLeptons leptonsFS_sel4l;
leptonsFS_sel4l.insert( leptonsFS_sel4l.end(), cand_mu.begin(), cand_mu.end() );
leptonsFS_sel4l.insert( leptonsFS_sel4l.end(), cand_e.begin(), cand_e.end() );
////////////
// Cut dR>0.2 between all leptons
Particles n_parts;
for (const DressedLepton& l1 : leptonsFS_sel4l) {
bool isolated = true;
for (DressedLepton& l2 : leptonsFS_sel4l){
const double fourL_dR = deltaR(l1, l2);
if (fourL_dR < 0.2 && !isSame(l1, l2)) {
isolated = false;
break;
}
}
if (isolated) n_parts.push_back(l1);
}
double totalCharge = 0;
for (const Particle& p : n_parts) totalCharge += p.pid();
if (n_parts.size() == 4 && totalCharge == 0 ) {
Zstate lead_Z, sub_Z;
identifyZstates(lead_Z, sub_Z, n_parts);
if (lead_Z.mom().pT() < sub_Z.mom().pT()) {
std::swap(lead_Z, sub_Z);
}
DressedLeptons lepton4l;
lepton4l.insert( lepton4l.end(), leptonsFS_sel4l.begin(), leptonsFS_sel4l.end() );
std::sort(lepton4l.begin(), lepton4l.end(), [](const DressedLepton& l1, const DressedLepton& l2) {
return (l1.abseta() > l2.abseta());
});
if (lead_Z.first.abspid() == 11 && sub_Z.first.abspid() == 11) {
if (lepton4l[1].abseta() > 2.5 || lepton4l[2].abseta() > 2.5 || lepton4l[3].abseta() > 2.5) vetoEvent;
}
else if (lead_Z.first.abspid() != sub_Z.first.abspid()) {
if (lead_Z.first.abspid() == 11) {
if (std::min(lead_Z.first.abseta(), lead_Z.second.abseta()) > 2.5) vetoEvent;
}
if (lead_Z.first.abspid() == 13) {
if (std::min(sub_Z.first.abseta(), sub_Z.second.abseta()) > 2.5) vetoEvent;
}
}
double m_Z1 = lead_Z.mom().mass();
double m_Z2 = sub_Z.mom().mass();
double lead_Z_Pt = lead_Z.mom().pT();
double lead_dPhi = lead_Z.dphi();
double ZZ_rap = fabs(lead_Z.rapid() - sub_Z.rapid());
//Z mass selections
if ( inRange(m_Z1, 66*GeV, 116*GeV) && inRange(m_Z2, 66*GeV, 116*GeV) ) {
_h["leading_ll_pt"]->fill(lead_Z_Pt/GeV);
_h["leading_ll_phi"]->fill(lead_dPhi);
_h["ZZ_rapidity"]->fill(ZZ_rap);
_h["Njets"]->fill(jets.size());
}
}
}
};
/// Normalise histograms etc., after the run
void finalize() {
// histo1D is divided by bin width when converting to scatter2D so no need of further normalisation for this one.
const double sf = crossSectionPerEvent()/femtobarn;
const double sf2 = crossSectionPerEvent()/picobarn;
// 4l is divided by branching ratio to make a ZZ cross section
const double br = (3.3632 + 3.3662)/100.;
scale(_h["leading_ll_pt"], sf/sqr(br));
scale(_h["Njets"], sf/sqr(br));
scale(_h["leading_ll_phi"], sf2/sqr(br));
scale(_h["ZZ_rapidity"], sf2/sqr(br));
// llvv is cross section for a single flavour. The analysis assumes we have run on e and mu,
// so divides by 2.
scale(_h["dilepton_pt"], sf/2.0);
scale(_h["mzz_lvchannel"], sf/2.0);
scale(_h["llphi_lvchannel"],sf/2.0);
}
private:
void identifyZstates(Zstate& Z1, Zstate& Z2, const Particles& n_parts);
const double Zmass = 91.1876*GeV; // GeV
map<string, Histo1DPtr> _h;
size_t _mode;
};
void ATLAS_2016_I1494075::identifyZstates(Zstate& Z1, Zstate& Z2, const Particles& n_parts){
// first find the lepton types
Particles part_pos_el, part_neg_el, part_pos_mu, part_neg_mu;
for (const Particle& l : n_parts) {
if (l.abspid() == PID::ELECTRON) {
if (l.pid() < 0) part_neg_el.push_back(l);
if (l.pid() > 0) part_pos_el.push_back(l);
}
else if (l.abspid() == PID::MUON) {
if (l.pid() < 0) part_neg_mu.push_back(l);
if (l.pid() > 0) part_pos_mu.push_back(l);
}
}
//4e/4mu channel, pairing ambiguity
if (part_neg_el.size() == 2 || part_neg_mu.size() == 2) {
Zstate Zcand1, Zcand2, Zcand3, Zcand4;
if (part_neg_el.size() == 2) {
Zcand1 = Zstate( ParticlePair( part_neg_el[0], part_pos_el[0] ) );
Zcand2 = Zstate( ParticlePair( part_neg_el[0], part_pos_el[1] ) );
Zcand3 = Zstate( ParticlePair( part_neg_el[1], part_pos_el[0] ) );
Zcand4 = Zstate( ParticlePair( part_neg_el[1], part_pos_el[1] ) );
} else {
Zcand1 = Zstate( ParticlePair( part_neg_mu[0], part_pos_mu[0] ) );
Zcand2 = Zstate( ParticlePair( part_neg_mu[0], part_pos_mu[1] ) );
Zcand3 = Zstate( ParticlePair( part_neg_mu[1], part_pos_mu[0] ) );
Zcand4 = Zstate( ParticlePair( part_neg_mu[1], part_pos_mu[1] ) );
}
// pairing should be |1 + 4| and |2 + 3| in mass order
double V1, V2;
V1 = fabs( Zcand1.mom().mass() - Zmass ) + fabs( Zcand4.mom().mass() - Zmass);
V2 = fabs( Zcand2.mom().mass() - Zmass ) + fabs( Zcand3.mom().mass() - Zmass);
if (V1 > V2) {
Z1 = Zcand2;
Z2 = Zcand3;
}
else {
Z1 = Zcand1;
Z2 = Zcand4;
}
//2e2mu
}
else if (part_neg_el.size() == 1 && part_neg_mu.size() == 1) {
Z1 = Zstate( ParticlePair( part_neg_mu[0], part_pos_mu[0] ) );
Z2 = Zstate( ParticlePair( part_neg_el[0], part_pos_el[0] ) );
}
}
RIVET_DECLARE_PLUGIN(ATLAS_2016_I1494075);
}