Page MenuHomeHEPForge

No OneTemporary

diff --git a/include/Rivet/Tools/SmearingFunctions.hh b/include/Rivet/Tools/SmearingFunctions.hh
--- a/include/Rivet/Tools/SmearingFunctions.hh
+++ b/include/Rivet/Tools/SmearingFunctions.hh
@@ -1,232 +1,380 @@
// -*- C++ -*-
#ifndef RIVET_SmearingFunctions_HH
#define RIVET_SmearingFunctions_HH
#include "Rivet/Particle.hh"
#include "Rivet/Jet.hh"
#include <random>
namespace Rivet {
/// Return a uniformly sampled random number between 0 and 1
/// @todo Move to (math?)utils
/// @todo Need to isolate random generators to a single thread
inline double rand01() {
//return rand() / (double)RAND_MAX;
static random_device rd;
static mt19937 gen(rd());
return generate_canonical<double, 10>(gen);
}
/// @name Standard particle efficiency and smearing functions
//@{
inline double PARTICLE_FN0(const Particle& p) { return 0; }
inline double PARTICLE_FN1(const Particle& p) { return 1; }
inline double P4_FN0(const FourMomentum& p) { return 0; }
inline double P4_FN1(const FourMomentum& p) { return 1; }
inline Particle PARTICLE_SMEAR_IDENTITY(const Particle& p) { return p; }
////////////////////////
inline double ELECTRON_TRKEFF_ATLAS_RUN1(const Particle& e) {
if (e.abseta() > 2.5) return 0;
if (e.pT() < 0.1*GeV) return 0;
if (e.abseta() < 1.5) {
if (e.pT() < 1*GeV) return 0.73;
if (e.pT() < 100*GeV) return 0.95;
return 0.99;
} else {
if (e.pT() < 1*GeV) return 0.50;
if (e.pT() < 100*GeV) return 0.83;
else return 0.90;
}
}
inline double ELECTRON_EFF_ATLAS_RUN1(const Particle& e) {
if (e.abseta() > 2.5) return 0;
if (e.pT() < 10*GeV) return 0;
return (e.abseta() < 1.5) ? 0.95 : 0.85;
}
inline Particle ELECTRON_SMEAR_ATLAS_RUN1(const Particle& e) {
/// @todo Need to isolate random generators to a single thread
static random_device rd;
static mt19937 gen(rd());
static const vector<double> edges_eta = {0., 2.5, 3., 5.};
static const vector<double> edges_pt = {0., 0.1, 25.};
static const vector<double> e2s = {0.000, 0.015, 0.005,
0.005, 0.005, 0.005,
0.107, 0.107, 0.107};
static const vector<double> es = {0.00, 0.00, 0.05,
0.05, 0.05, 0.05,
2.08, 2.08, 2.08};
static const vector<double> cs = {0.00, 0.00, 0.25,
0.25, 0.25, 0.25,
0.00, 0.00, 0.00};
const int i_eta = binIndex(e.abseta(), edges_eta, true);
const int i_pt = binIndex(e.pT()/GeV, edges_pt, true);
const int i = i_eta*edges_pt.size() + i_pt;
// Calculate absolute resolution in GeV
const double c1 = sqr(e2s[i]), c2 = sqr(es[i]), c3 = sqr(cs[i]);
const double resolution = sqrt(c1*e.E2() + c2*e.E() + c3) * GeV;
/// @todo Extract to a smear_energy helper function
/// @todo Also make smear_direction and smear_pt functions, and jet versions that also update/smear constituents
normal_distribution<> d(e.E(), resolution);
const double smeared_E = max(d(gen), e.mass()); //< can't let the energy go below the mass!
return Particle(e.pid(), FourMomentum::mkEtaPhiME(e.eta(), e.phi(), e.mass(), smeared_E));
}
+ inline double ELECTRON_TRKEFF_CMS_RUN1(const Particle& e) {
+ if (e.abseta() > 2.5) return 0;
+ if (e.pT() < 0.1*GeV) return 0;
+ if (e.abseta() < 1.5) {
+ return (e.pT() < 1*GeV) ? 0.70 : 0.95;
+ } else {
+ return (e.pT() < 1*GeV) ? 0.60 : 0.85;
+ }
+ }
+
+ inline double ELECTRON_EFF_CMS_RUN1(const Particle& e) {
+ if (e.abseta() > 2.5) return 0;
+ if (e.pT() < 10*GeV) return 0;
+ return (e.abseta() < 1.5) ? 0.95 : 0.85;
+ }
+
+
+ /// @brief CMS electron energy smearing, preserving direction
+ ///
+ /// Calculate resolution
+ /// for pT > 0.1 GeV, E resolution = |eta| < 0.5 -> sqrt(0.06^2 + pt^2 * 1.3e-3^2)
+ /// |eta| < 1.5 -> sqrt(0.10^2 + pt^2 * 1.7e-3^2)
+ /// |eta| < 2.5 -> sqrt(0.25^2 + pt^2 * 3.1e-3^2)
+ inline Particle ELECTRON_SMEAR_CMS_RUN1(const Particle& e) {
+ /// @todo Need to isolate random generators to a single thread
+ static random_device rd;
+ static mt19937 gen(rd());
+
+ // Calculate absolute resolution in GeV from functional form
+ double resolution = 0;
+ const double abseta = e.abseta();
+ if (e.pT() > 0.1*GeV && abseta < 2.5) { //< should be a given from efficiencies
+ if (abseta < 0.5) {
+ resolution = add_quad(0.06, 1.3e-3 * e.pT()/GeV) * GeV;
+ } else if (abseta < 1.5) {
+ resolution = add_quad(0.10, 1.7e-3 * e.pT()/GeV) * GeV;
+ } else { // still |eta| < 2.5
+ resolution = add_quad(0.25, 3.1e-3 * e.pT()/GeV) * GeV;
+ }
+ }
+
+ /// @todo Extract to a smear_energy helper function
+ normal_distribution<> d(e.E(), resolution);
+ const double smeared_E = max(d(gen), e.mass()); //< can't let the energy go below the mass!
+ return Particle(e.pid(), FourMomentum::mkEtaPhiME(e.eta(), e.phi(), e.mass(), smeared_E));
+ }
+
+
///////////////////
inline double MUON_TRKEFF_ATLAS_RUN1(const Particle& m) {
if (m.abseta() > 2.5) return 0;
if (m.pT() < 0.1*GeV) return 0;
if (m.abseta() < 1.5) {
return (m.pT() < 1*GeV) ? 0.75 : 0.99;
} else {
return (m.pT() < 1*GeV) ? 0.70 : 0.98;
}
}
inline double MUON_EFF_ATLAS_RUN1(const Particle& m) {
if (m.abseta() > 2.7) return 0;
if (m.pT() < 10*GeV) return 0;
return (m.abseta() < 1.5) ? 0.95 : 0.85;
}
inline Particle MUON_SMEAR_ATLAS_RUN1(const Particle& m) {
/// @todo Need to isolate random generators to a single thread
static random_device rd;
static mt19937 gen(rd());
static const vector<double> edges_eta = {0, 1.5, 2.5};
static const vector<double> edges_pt = {0, 0.1, 1.0, 10., 200.};
static const vector<double> res = {0., 0.03, 0.02, 0.03, 0.05,
0., 0.04, 0.03, 0.04, 0.05};
const int i_eta = binIndex(m.abseta(), edges_eta, true);
const int i_pt = binIndex(m.pT()/GeV, edges_pt, true);
const int i = i_eta*edges_pt.size() + i_pt;
const double resolution = res[i];
// Smear by a Gaussian centered on the current pT, with width given by the resolution
/// @todo Extract to a smear_pt helper function
normal_distribution<> d(m.pT(), resolution*m.pT());
const double smeared_pt = max(d(gen), 0.);
return Particle(m.pid(), FourMomentum::mkEtaPhiMPt(m.eta(), m.phi(), m.mass(), smeared_pt));
}
+ /// @note Eff values currently identical to those in ATLAS (AB, 2016-04-12)
+ inline double MUON_TRKEFF_CMS_RUN1(const Particle& m) {
+ if (m.abseta() > 2.5) return 0;
+ if (m.pT() < 0.1*GeV) return 0;
+ if (m.abseta() < 1.5) {
+ return (m.pT() < 1*GeV) ? 0.75 : 0.99;
+ } else {
+ return (m.pT() < 1*GeV) ? 0.70 : 0.98;
+ }
+ }
+
+
+ inline double MUON_EFF_CMS_RUN1(const Particle& m) {
+ if (m.abseta() > 2.4) return 0;
+ if (m.pT() < 10*GeV) return 0;
+ return 0.95 * (m.abseta() < 1.5 ? 1 : exp(0.5 - 5e-4*m.pT()/GeV));
+ }
+
+
+ inline Particle MUON_SMEAR_CMS_RUN1(const Particle& m) {
+ /// @todo Need to isolate random generators to a single thread
+ static random_device rd;
+ static mt19937 gen(rd());
+
+ // Calculate fractional resolution
+ // for pT > 0.1 GeV, mom resolution = |eta| < 0.5 -> sqrt(0.01^2 + pt^2 * 2.0e-4^2)
+ // |eta| < 1.5 -> sqrt(0.02^2 + pt^2 * 3.0e-4^2)
+ // |eta| < 2.5 -> sqrt(0.05^2 + pt^2 * 2.6e-4^2)
+ double resolution = 0;
+ const double abseta = m.abseta();
+ if (m.pT() > 0.1*GeV && abseta < 2.5) {
+ if (abseta < 0.5) {
+ resolution = add_quad(0.01, 2.0e-4 * m.pT()/GeV);
+ } else if (abseta < 1.5) {
+ resolution = add_quad(0.02, 3.0e-4 * m.pT()/GeV);
+ } else { // still |eta| < 2.5... but isn't CMS' mu acceptance < 2.4?
+ resolution = add_quad(0.05, 2.6e-4 * m.pT()/GeV);
+ }
+ }
+
+ // Smear by a Gaussian centered on the current pT, with width given by the resolution
+ /// @todo Extract to a smear_pt helper function
+ normal_distribution<> d(m.pT(), resolution*m.pT());
+ const double smeared_pt = max(d(gen), 0.);
+ return Particle(m.pid(), FourMomentum::mkEtaPhiMPt(m.eta(), m.phi(), m.mass(), smeared_pt));
+ }
+
+
//////////////////////////
+ /// @brief ATLAS Run 1 8 TeV tau efficiencies (medium working point)
+ ///
+ /// Taken from http://arxiv.org/pdf/1412.7086.pdf
+ /// 20-40 GeV 1-prong LMT eff|mis = 0.66|1/10, 0.56|1/20, 0.36|1/80
+ /// 20-40 GeV 3-prong LMT eff|mis = 0.45|1/60, 0.38|1/100, 0.27|1/300
+ /// > 40 GeV 1-prong LMT eff|mis = 0.66|1/15, 0.56|1/25, 0.36|1/80
+ /// > 40 GeV 3-prong LMT eff|mis = 0.45|1/250, 0.38|1/400, 0.27|1/1300
inline double TAU_EFF_ATLAS_RUN1(const Particle& t) {
- if (t.abseta() > 2.5) return 0;
- if (t.pT() < 1*GeV) return 0;
- /// @todo If leptonic return 0
- return 0.45;
+ if (t.abseta() > 2.5) return 0; //< hmm... mostly
+ double pThadvis = 0;
+ Particles chargedhadrons;
+ for (const Particle& p : t.children()) {
+ if (p.isHadron()) {
+ pThadvis += p.pT(); //< right definition? Paper is unclear
+ if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p;
+ }
+ }
+ if (chargedhadrons.empty()) return 0; //< leptonic tau
+ if (pThadvis < 20*GeV) return 0; //< below threshold
+ if (pThadvis < 40*GeV) {
+ if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.56 : 1/20.;
+ if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.38 : 1/100.;
+ } else {
+ if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.56 : 1/25.;
+ if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.38 : 1/400.;
+ }
+ return 0;
}
- /// From ATL-PHYS-PUB-2015-045 medium working point
+ /// @brief ATLAS Run 2 13 TeV tau efficiencies (medium working point)
+ ///
+ /// From https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/PUBNOTES/ATL-PHYS-PUB-2015-045/ATL-PHYS-PUB-2015-045.pdf
+ /// LMT 1 prong efficiency/mistag = 0.6|1/30, 0.55|1/50, 0.45|1/120
+ /// LMT 3 prong efficiency/mistag = 0.5|1/30, 0.4|1/110, 0.3|1/300
inline double TAU_EFF_ATLAS_RUN2(const Particle& t) {
- if (t.abseta() > 2.5) return 0;
- if (t.pT() < 1*GeV) return 0;
- /// @todo If leptonic return 0
- ///< @todo Make nProng-specific
- // 1 prong efficiency / mistag = 0.7 / 0.02
- // >= 2 prong efficiency / mistag = 0.6 / 0.01
- const vector<Particle> children = t.children();
- /// @todo Require prongs in tracker acceptance, too?
- const size_t nprongs = count_if(children.begin(), children.end(),
- [](const Particle& p){ return p.isHadron() && p.charge3() != 0; });
- if (nprongs == 0) return 0;
- if (nprongs == 1) return (t.abspid() == PID::TAU) ? 0.7 : 0.02;
- return (t.abspid() == PID::TAU) ? 0.6 : 0.01;
+ if (t.abseta() > 2.5) return 0; //< hmm... mostly
+ double pThadvis = 0;
+ Particles chargedhadrons;
+ for (const Particle& p : t.children()) {
+ if (p.isHadron()) {
+ pThadvis += p.pT(); //< right definition? Paper is unclear
+ if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p;
+ }
+ }
+ if (chargedhadrons.empty()) return 0; //< leptonic tau
+ if (pThadvis < 20*GeV) return 0; //< below threshold
+ if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.55 : 1/50.;
+ if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.40 : 1/110.;
+ return 0;
}
+ /// ATLAS Run 1 tau smearing
+ /// @todo Currently a copy of the crappy jet smearing that is probably wrong...
inline Particle TAU_SMEAR_ATLAS_RUN1(const Particle& t) {
/// @todo Need to isolate random generators to a single thread
static random_device rd;
static mt19937 gen(rd());
// Const fractional resolution for now
static const double resolution = 0.03;
// Smear by a Gaussian centered on 1 with width given by the (fractional) resolution
/// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction?
normal_distribution<> d(1., resolution);
const double fsmear = max(d(gen), 0.);
return Particle(t.pid(), FourMomentum::mkXYZM(t.px()*fsmear, t.py()*fsmear, t.pz()*fsmear, t.mass()));
}
- ///////////////////////
-
-
- inline Jet JET_SMEAR_ATLAS_RUN1(const Jet& j) {
- /// @todo Need to isolate random generators to a single thread
- static random_device rd;
- static mt19937 gen(rd());
-
- // Const fractional resolution for now
- static const double resolution = 0.03;
-
- // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution
- /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction?
- normal_distribution<> d(1., resolution);
- const double fsmear = max(d(gen), 0.);
- return Jet(FourMomentum::mkXYZM(j.px()*fsmear, j.py()*fsmear, j.pz()*fsmear, j.mass()));
+ /// CMS Run 2 tau efficiency
+ ///
+ /// @todo Needs work; this is the dumb version from Delphes 3.3.2
+ inline double TAU_EFF_CMS_RUN2(const Particle& t) {
+ return (t.abspid() == PID::TAU) ? 0.6 : 0;
}
+ /// CMS Run 1 tau smearing
+ /// @todo Currently a copy of the crappy ATLAS one
+ inline Particle TAU_SMEAR_CMS_RUN1(const Particle& t) {
+ return TAU_SMEAR_ATLAS_RUN1(t);
+ }
+
+ /// CMS Run 2 tau smearing
+ /// @todo Currently a copy of the Run 1 version
+ inline Particle TAU_SMEAR_CMS_RUN2(const Particle& t) {
+ return TAU_SMEAR_CMS_RUN1(t);
+ }
//@}
/// @name Standard jet efficiency and smearing functions
//@{
/// Return a constant 0 given a Jet as argument
inline double JET_EFF_ZERO(const Jet& p) { return 0; }
/// Return a constant 1 given a Jet as argument
inline double JET_EFF_ONE(const Jet& p) { return 1; }
/// Return 1 if the given Jet contains a b, otherwise 0
inline double JET_BTAG_PERFECT(const Jet& j) { return j.bTagged() ? 1 : 0; }
/// Return the ATLAS Run 1 jet flavour tagging efficiency for the given Jet
inline double JET_BTAG_ATLAS_RUN1(const Jet& j) {
if (j.bTagged()) return 0.80*tanh(0.003*j.pT()/GeV)*(30/(1+0.086*j.pT()/GeV));
if (j.cTagged()) return 0.20*tanh(0.02*j.pT()/GeV)*(1/(1+0.0034*j.pT()/GeV));
return 0.002 + 7.3e-6*j.pT()/GeV;
}
/// Return 1 if the given Jet contains a c, otherwise 0
inline double JET_CTAG_PERFECT(const Jet& j) { return j.cTagged() ? 1 : 0; }
/// Take a jet and return an unmodified copy
/// @todo Modify constituent particle vectors for consistency
/// @todo Set a null PseudoJet if the Jet is smeared?
inline Jet JET_SMEAR_IDENTITY(const Jet& j) { return j; }
+ /// ATLAS Run 1 jet smearing
+ /// @todo This is a cluster-level flat 3% resolution, I think, and smearing is suboptimal: improve!
+ inline Jet JET_SMEAR_ATLAS_RUN1(const Jet& j) {
+ /// @todo Need to isolate random generators to a single thread
+ static random_device rd;
+ static mt19937 gen(rd());
+
+ // Const fractional resolution for now
+ static const double resolution = 0.03;
+
+ // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution
+ /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction?
+ normal_distribution<> d(1., resolution);
+ const double fsmear = max(d(gen), 0.);
+ return Jet(FourMomentum::mkXYZM(j.px()*fsmear, j.py()*fsmear, j.pz()*fsmear, j.mass()));
+ }
+
+ /// CMS Run 1 jet smearing
+ /// @todo Just a copy of the suboptimal ATLAS one: improve!!
+ inline Jet JET_SMEAR_CMS_RUN1(const Jet& j) {
+ return JET_SMEAR_ATLAS_RUN1(j);
+ }
+
//@}
}
#endif

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 19, 6:49 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3805672
Default Alt Text
(15 KB)

Event Timeline