Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F7878741
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rRIVETHG rivethg
Event Timeline
Log In to Comment