SmearingFunctions.hh
Go to the documentation of this file.
00001 // -*- C++ -*- 00002 #ifndef RIVET_SmearingFunctions_HH 00003 #define RIVET_SmearingFunctions_HH 00004 00005 #include "Rivet/Particle.hh" 00006 #include "Rivet/Jet.hh" 00007 #include <random> 00008 00009 namespace Rivet { 00010 00011 00012 /// @name Random number and filtering utils 00013 //@{ 00014 00015 /// Return a uniformly sampled random number between 0 and 1 00016 /// @todo Move to (math?)utils 00017 /// @todo Need to isolate random generators to a single thread 00018 inline double rand01() { 00019 //return rand() / (double)RAND_MAX; 00020 static random_device rd; 00021 static mt19937 gen(rd()); 00022 return generate_canonical<double, 10>(gen); 00023 } 00024 00025 template <typename FN> 00026 inline bool efffilt(const Particle& p, FN& feff) { 00027 return rand01() < feff(p); 00028 } 00029 template <typename FN> 00030 inline bool efffilt(const Jet& j, FN& feff) { 00031 return rand01() < feff(j); 00032 } 00033 00034 struct ParticleEffFilter { 00035 template <typename FN> 00036 ParticleEffFilter(const FN& feff) : _feff(feff) {} 00037 bool operator () (const Particle& p) { return efffilt(p, _feff); } 00038 private: 00039 const std::function<bool(const Particle&)> _feff; 00040 }; 00041 using particleEffFilter = ParticleEffFilter; 00042 00043 struct JetEffFilter { 00044 template <typename FN> 00045 JetEffFilter(const FN& feff) : _feff(feff) {} 00046 bool operator () (const Jet& j) { return efffilt(j, _feff); } 00047 private: 00048 const std::function<bool(const Jet&)> _feff; 00049 }; 00050 using jetEffFilter = JetEffFilter; 00051 00052 //@} 00053 00054 00055 /// @name General particle & momentum efficiency and smearing functions 00056 //@{ 00057 00058 /// Take a Particle and return 0 00059 inline double PARTICLE_FN0(const Particle& p) { return 0; } 00060 /// Take a Particle and return 1 00061 inline double PARTICLE_FN1(const Particle& p) { return 1; } 00062 /// Take a Particle and return it unmodified 00063 inline Particle PARTICLE_SMEAR_IDENTITY(const Particle& p) { return p; } 00064 00065 00066 /// Take a FourMomentum and return 0 00067 inline double P4_FN0(const FourMomentum& p) { return 0; } 00068 /// Take a FourMomentum and return 1 00069 inline double P4_FN1(const FourMomentum& p) { return 1; } 00070 /// Take a FourMomentum and return it unmodified 00071 inline FourMomentum P4_SMEAR_IDENTITY(const FourMomentum& p) { return p; } 00072 00073 /// Smear a FourMomentum's energy using a Gaussian of absolute width @a resolution 00074 /// @todo Also make jet versions that update/smear constituents? 00075 inline FourMomentum P4_SMEAR_E_GAUSS(const FourMomentum& p, double resolution) { 00076 /// @todo Need to isolate random generators to a single thread 00077 static random_device rd; 00078 static mt19937 gen(rd()); 00079 normal_distribution<> d(p.E(), resolution); 00080 const double mass = p.mass2() > 0 ? p.mass() : 0; //< numerical carefulness... 00081 const double smeared_E = max(d(gen), mass); //< can't let the energy go below the mass! 00082 return FourMomentum::mkEtaPhiME(p.eta(), p.phi(), mass, smeared_E); 00083 } 00084 00085 /// Smear a FourMomentum's transverse momentum using a Gaussian of absolute width @a resolution 00086 inline FourMomentum P4_SMEAR_PT_GAUSS(const FourMomentum& p, double resolution) { 00087 /// @todo Need to isolate random generators to a single thread 00088 static random_device rd; 00089 static mt19937 gen(rd()); 00090 normal_distribution<> d(p.pT(), resolution); 00091 const double smeared_pt = max(d(gen), 0.); 00092 const double mass = p.mass2() > 0 ? p.mass() : 0; //< numerical carefulness... 00093 return FourMomentum::mkEtaPhiMPt(p.eta(), p.phi(), mass, smeared_pt); 00094 } 00095 00096 /// Smear a FourMomentum's mass using a Gaussian of absolute width @a resolution 00097 inline FourMomentum P4_SMEAR_MASS_GAUSS(const FourMomentum& p, double resolution) { 00098 /// @todo Need to isolate random generators to a single thread 00099 static random_device rd; 00100 static mt19937 gen(rd()); 00101 normal_distribution<> d(p.mass(), resolution); 00102 const double smeared_mass = max(d(gen), 0.); 00103 return FourMomentum::mkEtaPhiMPt(p.eta(), p.phi(), smeared_mass, p.pT()); 00104 } 00105 00106 00107 /// Take a Vector3 and return 0 00108 inline double P3_FN0(const Vector3& p) { return 0; } 00109 /// Take a Vector3 and return 1 00110 inline double P3_FN1(const Vector3& p) { return 1; } 00111 /// Take a Vector3 and return it unmodified 00112 inline Vector3 P3_SMEAR_IDENTITY(const Vector3& p) { return p; } 00113 00114 /// Smear a Vector3's length using a Gaussian of absolute width @a resolution 00115 inline Vector3 P3_SMEAR_LEN_GAUSS(const Vector3& p, double resolution) { 00116 /// @todo Need to isolate random generators to a single thread 00117 static random_device rd; 00118 static mt19937 gen(rd()); 00119 normal_distribution<> d(p.mod(), resolution); 00120 const double smeared_mod = max(d(gen), 0.); //< can't let the energy go below the mass! 00121 return smeared_mod * p.unit(); 00122 } 00123 00124 //@} 00125 00126 00127 /// @name Electron efficiency and smearing functions 00128 //@{ 00129 00130 /// ATLAS Run 1 electron tracking efficiency 00131 /// @todo How to use this in combination with reco eff? 00132 inline double ELECTRON_TRKEFF_ATLAS_RUN1(const Particle& e) { 00133 if (e.abseta() > 2.5) return 0; 00134 if (e.pT() < 0.1*GeV) return 0; 00135 if (e.abseta() < 1.5) { 00136 if (e.pT() < 1*GeV) return 0.73; 00137 if (e.pT() < 100*GeV) return 0.95; 00138 return 0.99; 00139 } else { 00140 if (e.pT() < 1*GeV) return 0.50; 00141 if (e.pT() < 100*GeV) return 0.83; 00142 else return 0.90; 00143 } 00144 } 00145 00146 /// ATLAS Run 2 electron tracking efficiency 00147 /// @todo Currently just a copy of Run 1: fix! 00148 inline double ELECTRON_TRKEFF_ATLAS_RUN2(const Particle& e) { 00149 return ELECTRON_TRKEFF_ATLAS_RUN1(e); 00150 } 00151 00152 00153 /// ATLAS Run 1 electron reconstruction efficiency 00154 /// @todo Include reco eff (but no e/y discrimination) in forward region 00155 /// @todo How to use this in combination with tracking eff? 00156 inline double ELECTRON_EFF_ATLAS_RUN1(const Particle& e) { 00157 if (e.abseta() > 2.5) return 0; 00158 if (e.pT() < 10*GeV) return 0; 00159 return (e.abseta() < 1.5) ? 0.95 : 0.85; 00160 } 00161 00162 /// ATLAS Run 2 electron reco efficiency 00163 /// @todo Currently just a copy of Run 1: fix! 00164 inline double ELECTRON_EFF_ATLAS_RUN2(const Particle& e) { 00165 return ELECTRON_EFF_ATLAS_RUN1(e); 00166 } 00167 00168 00169 /// @brief ATLAS Run 2 'loose' electron identification/selection efficiency 00170 /// 00171 /// Values read from Fig 3 of ATL-PHYS-PUB-2015-041 00172 /// @todo What about faking by jets or non-electrons? 00173 inline double ELECTRON_IDEFF_ATLAS_RUN2_LOOSE(const Particle& e) { 00174 00175 // Manually symmetrised eta eff histogram 00176 const static vector<double> edges_eta = { 0.0, 0.1, 0.8, 1.37, 1.52, 2.01, 2.37, 2.47 }; 00177 const static vector<double> effs_eta = { 0.950, 0.965, 0.955, 0.885, 0.950, 0.935, 0.90 }; 00178 // Et eff histogram (10-20 is a guess) 00179 const static vector<double> edges_et = { 0, 10, 20, 25, 30, 35, 40, 45, 50, 60, 80 }; 00180 const static vector<double> effs_et = { 0.0, 0.90, 0.91, 0.92, 0.94, 0.95, 0.955, 0.965, 0.97, 0.98 }; 00181 00182 if (e.abseta() > 2.47) return 0.0; // no ID outside the tracker 00183 00184 const int i_eta = binIndex(e.abseta(), edges_eta); 00185 const int i_et = binIndex(e.Et()/GeV, edges_et, true); 00186 const double eff = effs_et[i_et] * effs_eta[i_eta] / 0.95; //< norm factor as approximate double differential 00187 return min(eff, 1.0); 00188 } 00189 00190 00191 /// @brief ATLAS Run 1 'medium' electron identification/selection efficiency 00192 inline double ELECTRON_IDEFF_ATLAS_RUN1_MEDIUM(const Particle& e) { 00193 00194 const static vector<double> eta_edges_10 = {0.000, 0.049, 0.454, 1.107, 1.46, 1.790, 2.277, 2.500}; 00195 const static vector<double> eta_vals_10 = {0.730, 0.757, 0.780, 0.771, 0.77, 0.777, 0.778}; 00196 00197 const static vector<double> eta_edges_15 = {0.000, 0.053, 0.456, 1.102, 1.463, 1.783, 2.263, 2.500}; 00198 const static vector<double> eta_vals_15 = {0.780, 0.800, 0.819, 0.759, 0.749, 0.813, 0.829}; 00199 00200 const static vector<double> eta_edges_20 = {0.000, 0.065, 0.362, 0.719, 0.980, 1.289, 1.455, 1.681, 1.942, 2.239, 2.452, 2.500}; 00201 const static vector<double> eta_vals_20 = {0.794, 0.806, 0.816, 0.806, 0.797, 0.774, 0.764, 0.788, 0.793, 0.806, 0.825}; 00202 00203 const static vector<double> eta_edges_25 = {0.000, 0.077, 0.338, 0.742, 1.004, 1.265, 1.467, 1.692, 1.940, 2.227, 2.452, 2.500}; 00204 const static vector<double> eta_vals_25 = {0.833, 0.843, 0.853, 0.845, 0.839, 0.804, 0.790, 0.825, 0.830, 0.833, 0.839}; 00205 00206 const static vector<double> eta_edges_30 = {0.000, 0.077, 0.350, 0.707, 0.980, 1.289, 1.479, 1.681, 1.942, 2.239, 2.441, 2.500}; 00207 const static vector<double> eta_vals_30 = {0.863, 0.872, 0.881, 0.874, 0.870, 0.824, 0.808, 0.847, 0.845, 0.840, 0.842}; 00208 00209 const static vector<double> eta_edges_35 = {0.000, 0.058, 0.344, 0.700, 1.009, 1.270, 1.458, 1.685, 1.935, 2.231, 2.468, 2.500}; 00210 const static vector<double> eta_vals_35 = {0.878, 0.889, 0.901, 0.895, 0.893, 0.849, 0.835, 0.868, 0.863, 0.845, 0.832}; 00211 00212 const static vector<double> eta_edges_40 = {0.000, 0.047, 0.355, 0.699, 0.983, 1.280, 1.446, 1.694, 1.943, 2.227, 2.441, 2.500}; 00213 const static vector<double> eta_vals_40 = {0.894, 0.901, 0.909, 0.905, 0.904, 0.875, 0.868, 0.889, 0.876, 0.848, 0.827}; 00214 00215 const static vector<double> eta_edges_45 = {0.000, 0.058, 0.356, 0.712, 0.997, 1.282, 1.459, 1.686, 1.935, 2.220, 2.444, 2.500}; 00216 const static vector<double> eta_vals_45 = {0.900, 0.911, 0.923, 0.918, 0.917, 0.897, 0.891, 0.904, 0.894, 0.843, 0.796}; 00217 00218 const static vector<double> eta_edges_50 = {0.000, 0.059, 0.355, 0.711, 0.983, 1.280, 1.469, 1.682, 1.919, 2.227, 2.441, 2.500}; 00219 const static vector<double> eta_vals_50 = {0.903, 0.913, 0.923, 0.922, 0.923, 0.903, 0.898, 0.908, 0.895, 0.831, 0.774}; 00220 00221 const static vector<double> eta_edges_60 = {0.000, 0.053, 0.351, 0.720, 1.006, 1.291, 1.469, 1.696, 1.946, 2.243, 2.455, 2.500}; 00222 const static vector<double> eta_vals_60 = {0.903, 0.917, 0.928, 0.924, 0.927, 0.915, 0.911, 0.915, 0.899, 0.827, 0.760}; 00223 00224 const static vector<double> eta_edges_80 = {0.000, 0.053, 0.351, 0.720, 0.994, 1.292, 1.482, 1.708, 1.934, 2.220, 2.458, 2.500}; 00225 const static vector<double> eta_vals_80 = {0.936, 0.942, 0.952, 0.956, 0.956, 0.934, 0.931, 0.944, 0.933, 0.940, 0.948}; 00226 00227 const static vector<double> et_edges = { 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 80 }; 00228 const static vector< vector<double> > et_eta_edges = { eta_edges_10, eta_edges_15, eta_edges_20, eta_edges_25, eta_edges_30, eta_edges_35, eta_edges_40, eta_edges_45, eta_edges_50, eta_edges_60, eta_edges_80 }; 00229 const static vector< vector<double> > et_eta_vals = { eta_vals_10, eta_vals_15, eta_vals_20, eta_vals_25, eta_vals_30, eta_vals_35, eta_vals_40, eta_vals_45, eta_vals_50, eta_vals_60, eta_vals_80 }; 00230 00231 if (e.abseta() > 2.5 || e.Et() < 10*GeV) return 0.0; 00232 const int i_et = binIndex(e.Et()/GeV, et_edges, true); 00233 const int i_eta = binIndex(e.abseta(), et_eta_edges[i_et]); 00234 return et_eta_vals[i_et][i_eta]; 00235 } 00236 00237 /// @brief ATLAS Run 2 'medium' electron identification/selection efficiency 00238 /// @todo Currently just a copy of Run 1: fix! 00239 inline double ELECTRON_IDEFF_ATLAS_RUN2_MEDIUM(const Particle& e) { 00240 return ELECTRON_IDEFF_ATLAS_RUN1_MEDIUM(e); 00241 } 00242 00243 00244 /// @brief ATLAS Run 1 'tight' electron identification/selection efficiency 00245 inline double ELECTRON_IDEFF_ATLAS_RUN1_TIGHT(const Particle& e) { 00246 00247 const static vector<double> eta_edges_10 = {0.000, 0.049, 0.459, 1.100, 1.461, 1.789, 2.270, 2.500}; 00248 const static vector<double> eta_vals_10 = {0.581, 0.632, 0.668, 0.558, 0.548, 0.662, 0.690}; 00249 00250 const static vector<double> eta_edges_15 = {0.000, 0.053, 0.450, 1.096, 1.463, 1.783, 2.269, 2.500}; 00251 const static vector<double> eta_vals_15 = {0.630, 0.678, 0.714, 0.633, 0.616, 0.700, 0.733}; 00252 00253 const static vector<double> eta_edges_20 = {0.000, 0.065, 0.362, 0.719, 0.992, 1.277, 1.479, 1.692, 1.930, 2.227, 2.464, 2.500}; 00254 const static vector<double> eta_vals_20 = {0.653, 0.695, 0.735, 0.714, 0.688, 0.635, 0.625, 0.655, 0.680, 0.691, 0.674}; 00255 00256 const static vector<double> eta_edges_25 = {0.000, 0.077, 0.362, 0.719, 0.992, 1.300, 1.479, 1.692, 1.942, 2.227, 2.464, 2.500}; 00257 const static vector<double> eta_vals_25 = {0.692, 0.732, 0.768, 0.750, 0.726, 0.677, 0.667, 0.692, 0.710, 0.706, 0.679}; 00258 00259 const static vector<double> eta_edges_30 = {0.000, 0.053, 0.362, 0.719, 1.004, 1.277, 1.467, 1.681, 1.954, 2.239, 2.452, 2.500}; 00260 const static vector<double> eta_vals_30 = {0.724, 0.763, 0.804, 0.789, 0.762, 0.702, 0.690, 0.720, 0.731, 0.714, 0.681}; 00261 00262 const static vector<double> eta_edges_35 = {0.000, 0.044, 0.342, 0.711, 0.971, 1.280, 1.456, 1.683, 1.944, 2.218, 2.442, 2.500}; 00263 const static vector<double> eta_vals_35 = {0.736, 0.778, 0.824, 0.811, 0.784, 0.730, 0.718, 0.739, 0.743, 0.718, 0.678}; 00264 00265 const static vector<double> eta_edges_40 = {0.000, 0.047, 0.355, 0.699, 0.983, 1.268, 1.457, 1.671, 1.931, 2.204, 2.453, 2.500}; 00266 const static vector<double> eta_vals_40 = {0.741, 0.774, 0.823, 0.823, 0.802, 0.764, 0.756, 0.771, 0.771, 0.734, 0.684}; 00267 00268 const static vector<double> eta_edges_45 = {0.000, 0.056, 0.354, 0.711, 0.984, 1.280, 1.458, 1.684, 1.945, 2.207, 2.442, 2.500}; 00269 const static vector<double> eta_vals_45 = {0.758, 0.792, 0.841, 0.841, 0.823, 0.792, 0.786, 0.796, 0.794, 0.734, 0.663}; 00270 00271 const static vector<double> eta_edges_50 = {0.000, 0.059, 0.355, 0.699, 0.983, 1.268, 1.446, 1.682, 1.943, 2.216, 2.453, 2.500}; 00272 const static vector<double> eta_vals_50 = {0.771, 0.806, 0.855, 0.858, 0.843, 0.810, 0.800, 0.808, 0.802, 0.730, 0.653}; 00273 00274 const static vector<double> eta_edges_60 = {0.000, 0.050, 0.350, 0.707, 0.981, 1.278, 1.468, 1.694, 1.944, 2.242, 2.453, 2.500}; 00275 const static vector<double> eta_vals_60 = {0.773, 0.816, 0.866, 0.865, 0.853, 0.820, 0.812, 0.817, 0.804, 0.726, 0.645}; 00276 00277 const static vector<double> eta_edges_80 = {0.000, 0.051, 0.374, 0.720, 0.981, 1.279, 1.468, 1.707, 1.945, 2.207, 2.457, 2.500}; 00278 const static vector<double> eta_vals_80 = {0.819, 0.855, 0.899, 0.906, 0.900, 0.869, 0.865, 0.873, 0.869, 0.868, 0.859}; 00279 00280 const static vector<double> et_edges = { 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 80 }; 00281 const static vector< vector<double> > et_eta_edges = { eta_edges_10, eta_edges_15, eta_edges_20, eta_edges_25, eta_edges_30, eta_edges_35, eta_edges_40, eta_edges_45, eta_edges_50, eta_edges_60, eta_edges_80 }; 00282 const static vector< vector<double> > et_eta_vals = { eta_vals_10, eta_vals_15, eta_vals_20, eta_vals_25, eta_vals_30, eta_vals_35, eta_vals_40, eta_vals_45, eta_vals_50, eta_vals_60, eta_vals_80 }; 00283 00284 if (e.abseta() > 2.5 || e.Et() < 10*GeV) return 0.0; 00285 const int i_et = binIndex(e.Et()/GeV, et_edges, true); 00286 const int i_eta = binIndex(e.abseta(), et_eta_edges[i_et]); 00287 return et_eta_vals[i_et][i_eta]; 00288 } 00289 00290 /// @brief ATLAS Run 2 'tight' electron identification/selection efficiency 00291 /// @todo Currently just a copy of Run 1: fix! 00292 inline double ELECTRON_IDEFF_ATLAS_RUN2_TIGHT(const Particle& e) { 00293 return ELECTRON_IDEFF_ATLAS_RUN1_TIGHT(e); 00294 } 00295 00296 00297 00298 /// ATLAS Run 1 electron reco smearing 00299 inline Particle ELECTRON_SMEAR_ATLAS_RUN1(const Particle& e) { 00300 static const vector<double> edges_eta = {0., 2.5, 3.}; 00301 static const vector<double> edges_pt = {0., 0.1, 25.}; 00302 static const vector<double> e2s = {0.000, 0.015, 0.005, 00303 0.005, 0.005, 0.005, 00304 0.107, 0.107, 0.107}; 00305 static const vector<double> es = {0.00, 0.00, 0.05, 00306 0.05, 0.05, 0.05, 00307 2.08, 2.08, 2.08}; 00308 static const vector<double> cs = {0.00, 0.00, 0.25, 00309 0.25, 0.25, 0.25, 00310 0.00, 0.00, 0.00}; 00311 00312 const int i_eta = binIndex(e.abseta(), edges_eta, true); 00313 const int i_pt = binIndex(e.pT()/GeV, edges_pt, true); 00314 const int i = i_eta*edges_pt.size() + i_pt; 00315 00316 // Calculate absolute resolution in GeV 00317 const double c1 = sqr(e2s[i]), c2 = sqr(es[i]), c3 = sqr(cs[i]); 00318 const double resolution = sqrt(c1*e.E2() + c2*e.E() + c3) * GeV; 00319 00320 // normal_distribution<> d(e.E(), resolution); 00321 // const double mass = e.mass2() > 0 ? e.mass() : 0; //< numerical carefulness... 00322 // const double smeared_E = max(d(gen), mass); //< can't let the energy go below the mass! 00323 // return Particle(e.pid(), FourMomentum::mkEtaPhiME(e.eta(), e.phi(), mass, smeared_E)); 00324 return Particle(e.pid(), P4_SMEAR_E_GAUSS(e, resolution)); 00325 } 00326 00327 00328 /// ATLAS Run 2 electron reco smearing 00329 /// @todo Currently just a copy of the Run 1 version: fix! 00330 inline Particle ELECTRON_SMEAR_ATLAS_RUN2(const Particle& e) { 00331 return ELECTRON_SMEAR_ATLAS_RUN1(e); 00332 } 00333 00334 00335 /// @todo Add charge flip efficiency? 00336 00337 00338 /// CMS Run 1 electron tracking efficiency 00339 /// @todo How to use this in combination with reco eff? 00340 inline double ELECTRON_TRKEFF_CMS_RUN1(const Particle& e) { 00341 if (e.abseta() > 2.5) return 0; 00342 if (e.pT() < 0.1*GeV) return 0; 00343 if (e.abseta() < 1.5) { 00344 return (e.pT() < 1*GeV) ? 0.70 : 0.95; 00345 } else { 00346 return (e.pT() < 1*GeV) ? 0.60 : 0.85; 00347 } 00348 } 00349 00350 00351 /// CMS Run 2 electron tracking efficiency 00352 /// @todo Currently just a copy of Run 1: fix! 00353 inline double ELECTRON_TRKEFF_CMS_RUN2(const Particle& e) { 00354 return ELECTRON_TRKEFF_CMS_RUN1(e); 00355 } 00356 00357 00358 /// CMS Run 1 electron reconstruction efficiency 00359 /// @todo How to use this in combination with tracking eff? 00360 inline double ELECTRON_EFF_CMS_RUN1(const Particle& e) { 00361 if (e.abseta() > 2.5) return 0; 00362 if (e.pT() < 10*GeV) return 0; 00363 return (e.abseta() < 1.5) ? 0.95 : 0.85; 00364 } 00365 00366 00367 /// CMS Run 2 electron reco efficiency 00368 /// @todo Currently just a copy of Run 1: fix! 00369 inline double ELECTRON_EFF_CMS_RUN2(const Particle& e) { 00370 return ELECTRON_EFF_CMS_RUN1(e); 00371 } 00372 00373 00374 /// @brief CMS electron energy smearing, preserving direction 00375 /// 00376 /// Calculate resolution 00377 /// for pT > 0.1 GeV, E resolution = |eta| < 0.5 -> sqrt(0.06^2 + pt^2 * 1.3e-3^2) 00378 /// |eta| < 1.5 -> sqrt(0.10^2 + pt^2 * 1.7e-3^2) 00379 /// |eta| < 2.5 -> sqrt(0.25^2 + pt^2 * 3.1e-3^2) 00380 inline Particle ELECTRON_SMEAR_CMS_RUN1(const Particle& e) { 00381 // Calculate absolute resolution in GeV from functional form 00382 double resolution = 0; 00383 const double abseta = e.abseta(); 00384 if (e.pT() > 0.1*GeV && abseta < 2.5) { //< should be a given from efficiencies 00385 if (abseta < 0.5) { 00386 resolution = add_quad(0.06, 1.3e-3 * e.pT()/GeV) * GeV; 00387 } else if (abseta < 1.5) { 00388 resolution = add_quad(0.10, 1.7e-3 * e.pT()/GeV) * GeV; 00389 } else { // still |eta| < 2.5 00390 resolution = add_quad(0.25, 3.1e-3 * e.pT()/GeV) * GeV; 00391 } 00392 } 00393 00394 // normal_distribution<> d(e.E(), resolution); 00395 // const double mass = e.mass2() > 0 ? e.mass() : 0; //< numerical carefulness... 00396 // const double smeared_E = max(d(gen), mass); //< can't let the energy go below the mass! 00397 // return Particle(e.pid(), FourMomentum::mkEtaPhiME(e.eta(), e.phi(), mass, smeared_E)); 00398 return Particle(e.pid(), P4_SMEAR_E_GAUSS(e, resolution)); 00399 } 00400 00401 00402 /// CMS Run 2 electron reco smearing 00403 /// @todo Currently just a copy of the Run 1 version: fix! 00404 inline Particle ELECTRON_SMEAR_CMS_RUN2(const Particle& e) { 00405 return ELECTRON_SMEAR_CMS_RUN1(e); 00406 } 00407 00408 //@} 00409 00410 00411 00412 /// @name Photon efficiency and smearing functions 00413 //@{ 00414 00415 /// @todo Photon efficiency and smearing 00416 00417 //@} 00418 00419 00420 00421 /// @name Muon efficiency and smearing functions 00422 //@{ 00423 00424 /// ATLAS Run 1 muon tracking efficiency 00425 /// @todo How to use this in combination with reco eff? 00426 inline double MUON_TRKEFF_ATLAS_RUN1(const Particle& m) { 00427 if (m.abseta() > 2.5) return 0; 00428 if (m.pT() < 0.1*GeV) return 0; 00429 if (m.abseta() < 1.5) { 00430 return (m.pT() < 1*GeV) ? 0.75 : 0.99; 00431 } else { 00432 return (m.pT() < 1*GeV) ? 0.70 : 0.98; 00433 } 00434 } 00435 00436 /// ATLAS Run 2 muon tracking efficiency 00437 /// @todo Currently just a copy of Run 1: fix! 00438 inline double MUON_TRKEFF_ATLAS_RUN2(const Particle& m) { 00439 return MUON_TRKEFF_ATLAS_RUN1(m); 00440 } 00441 00442 00443 /// ATLAS Run 1 muon reco efficiency 00444 /// @todo How to use this in combination with tracking eff? 00445 inline double MUON_EFF_ATLAS_RUN1(const Particle& m) { 00446 if (m.abseta() > 2.7) return 0; 00447 if (m.pT() < 10*GeV) return 0; 00448 return (m.abseta() < 1.5) ? 0.95 : 0.85; 00449 } 00450 00451 /// ATLAS Run 2 muon reco efficiency 00452 /// @todo Currently just a copy of Run 1: fix! 00453 inline double MUON_EFF_ATLAS_RUN2(const Particle& m) { 00454 return MUON_EFF_ATLAS_RUN1(m); 00455 } 00456 00457 00458 /// ATLAS Run 1 muon reco smearing 00459 inline Particle MUON_SMEAR_ATLAS_RUN1(const Particle& m) { 00460 static const vector<double> edges_eta = {0, 1.5, 2.5}; 00461 static const vector<double> edges_pt = {0, 0.1, 1.0, 10., 200.}; 00462 static const vector<double> res = {0., 0.03, 0.02, 0.03, 0.05, 00463 0., 0.04, 0.03, 0.04, 0.05}; 00464 00465 const int i_eta = binIndex(m.abseta(), edges_eta, true); 00466 const int i_pt = binIndex(m.pT()/GeV, edges_pt, true); 00467 const int i = i_eta*edges_pt.size() + i_pt; 00468 00469 const double resolution = res[i]; 00470 00471 // Smear by a Gaussian centered on the current pT, with width given by the resolution 00472 // normal_distribution<> d(m.pT(), resolution*m.pT()); 00473 // const double smeared_pt = max(d(gen), 0.); 00474 // const double mass = m.mass2() > 0 ? m.mass() : 0; //< numerical carefulness... 00475 // return Particle(m.pid(), FourMomentum::mkEtaPhiMPt(m.eta(), m.phi(), mass, smeared_pt)); 00476 return Particle(m.pid(), P4_SMEAR_PT_GAUSS(m, resolution*m.pT())); 00477 } 00478 00479 /// ATLAS Run 2 muon reco smearing 00480 /// @todo Currently just a copy of the Run 1 version: fix! 00481 inline Particle MUON_SMEAR_ATLAS_RUN2(const Particle& m) { 00482 return MUON_SMEAR_ATLAS_RUN1(m); 00483 } 00484 00485 00486 /// CMS Run 1 muon tracking efficiency 00487 /// @todo How to use this in combination with reco eff? 00488 /// @note Eff values currently identical to those in ATLAS (AB, 2016-04-12) 00489 inline double MUON_TRKEFF_CMS_RUN1(const Particle& m) { 00490 if (m.abseta() > 2.5) return 0; 00491 if (m.pT() < 0.1*GeV) return 0; 00492 if (m.abseta() < 1.5) { 00493 return (m.pT() < 1*GeV) ? 0.75 : 0.99; 00494 } else { 00495 return (m.pT() < 1*GeV) ? 0.70 : 0.98; 00496 } 00497 } 00498 00499 /// CMS Run 2 muon tracking efficiency 00500 /// @todo Currently just a copy of Run 1: fix! 00501 inline double MUON_TRKEFF_CMS_RUN2(const Particle& m) { 00502 return MUON_TRKEFF_CMS_RUN1(m); 00503 } 00504 00505 00506 /// CMS Run 1 muon reco efficiency 00507 /// @todo How to use this in combination with tracking eff? 00508 inline double MUON_EFF_CMS_RUN1(const Particle& m) { 00509 if (m.abseta() > 2.4) return 0; 00510 if (m.pT() < 10*GeV) return 0; 00511 return 0.95 * (m.abseta() < 1.5 ? 1 : exp(0.5 - 5e-4*m.pT()/GeV)); 00512 } 00513 00514 /// CMS Run 2 muon reco efficiency 00515 /// @todo Currently just a copy of Run 1: fix! 00516 inline double MUON_EFF_CMS_RUN2(const Particle& m) { 00517 return MUON_EFF_CMS_RUN1(m); 00518 } 00519 00520 00521 /// CMS Run 1 muon reco smearing 00522 inline Particle MUON_SMEAR_CMS_RUN1(const Particle& m) { 00523 // Calculate fractional resolution 00524 // for pT > 0.1 GeV, mom resolution = |eta| < 0.5 -> sqrt(0.01^2 + pt^2 * 2.0e-4^2) 00525 // |eta| < 1.5 -> sqrt(0.02^2 + pt^2 * 3.0e-4^2) 00526 // |eta| < 2.5 -> sqrt(0.05^2 + pt^2 * 2.6e-4^2) 00527 double resolution = 0; 00528 const double abseta = m.abseta(); 00529 if (m.pT() > 0.1*GeV && abseta < 2.5) { 00530 if (abseta < 0.5) { 00531 resolution = add_quad(0.01, 2.0e-4 * m.pT()/GeV); 00532 } else if (abseta < 1.5) { 00533 resolution = add_quad(0.02, 3.0e-4 * m.pT()/GeV); 00534 } else { // still |eta| < 2.5... but isn't CMS' mu acceptance < 2.4? 00535 resolution = add_quad(0.05, 2.6e-4 * m.pT()/GeV); 00536 } 00537 } 00538 00539 // Smear by a Gaussian centered on the current pT, with width given by the resolution 00540 // normal_distribution<> d(m.pT(), resolution*m.pT()); 00541 // const double smeared_pt = max(d(gen), 0.); 00542 // const double mass = m.mass2() > 0 ? m.mass() : 0; //< numerical carefulness... 00543 // return Particle(m.pid(), FourMomentum::mkEtaPhiMPt(m.eta(), m.phi(), mass, smeared_pt)); 00544 return Particle(m.pid(), P4_SMEAR_PT_GAUSS(m, resolution*m.pT())); 00545 } 00546 00547 /// CMS Run 2 muon reco smearing 00548 /// @todo Currently just a copy of the Run 1 version: fix! 00549 inline Particle MUON_SMEAR_CMS_RUN2(const Particle& m) { 00550 return MUON_SMEAR_CMS_RUN1(m); 00551 } 00552 00553 //@} 00554 00555 00556 00557 /// @name Tau efficiency and smearing functions 00558 //@{ 00559 00560 /// @brief ATLAS Run 1 8 TeV tau efficiencies (medium working point) 00561 /// 00562 /// Taken from http://arxiv.org/pdf/1412.7086.pdf 00563 /// 20-40 GeV 1-prong LMT eff|mis = 0.66|1/10, 0.56|1/20, 0.36|1/80 00564 /// 20-40 GeV 3-prong LMT eff|mis = 0.45|1/60, 0.38|1/100, 0.27|1/300 00565 /// > 40 GeV 1-prong LMT eff|mis = 0.66|1/15, 0.56|1/25, 0.36|1/80 00566 /// > 40 GeV 3-prong LMT eff|mis = 0.45|1/250, 0.38|1/400, 0.27|1/1300 00567 inline double TAU_EFF_ATLAS_RUN1(const Particle& t) { 00568 if (t.abseta() > 2.5) return 0; //< hmm... mostly 00569 double pThadvis = 0; 00570 Particles chargedhadrons; 00571 for (const Particle& p : t.children()) { 00572 if (p.isHadron()) { 00573 pThadvis += p.pT(); //< right definition? Paper is unclear 00574 if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p; 00575 } 00576 } 00577 if (chargedhadrons.empty()) return 0; //< leptonic tau 00578 if (pThadvis < 20*GeV) return 0; //< below threshold 00579 if (pThadvis < 40*GeV) { 00580 if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.56 : 1/20.; 00581 if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.38 : 1/100.; 00582 } else { 00583 if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.56 : 1/25.; 00584 if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.38 : 1/400.; 00585 } 00586 return 0; 00587 } 00588 00589 00590 /// @brief ATLAS Run 2 13 TeV tau efficiencies (medium working point) 00591 /// 00592 /// From https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/PUBNOTES/ATL-PHYS-PUB-2015-045/ATL-PHYS-PUB-2015-045.pdf 00593 /// LMT 1 prong efficiency/mistag = 0.6|1/30, 0.55|1/50, 0.45|1/120 00594 /// LMT 3 prong efficiency/mistag = 0.5|1/30, 0.4|1/110, 0.3|1/300 00595 inline double TAU_EFF_ATLAS_RUN2(const Particle& t) { 00596 if (t.abseta() > 2.5) return 0; //< hmm... mostly 00597 double pThadvis = 0; 00598 Particles chargedhadrons; 00599 for (const Particle& p : t.children()) { 00600 if (p.isHadron()) { 00601 pThadvis += p.pT(); //< right definition? Paper is unclear 00602 if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p; 00603 } 00604 } 00605 if (chargedhadrons.empty()) return 0; //< leptonic tau 00606 if (pThadvis < 20*GeV) return 0; //< below threshold 00607 if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.55 : 1/50.; 00608 if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.40 : 1/110.; 00609 return 0; 00610 } 00611 00612 00613 /// ATLAS Run 1 tau smearing 00614 /// @todo Currently a copy of the crappy jet smearing that is probably wrong... 00615 inline Particle TAU_SMEAR_ATLAS_RUN1(const Particle& t) { 00616 // Const fractional resolution for now 00617 static const double resolution = 0.03; 00618 00619 // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution 00620 /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction? 00621 /// @todo Need to isolate random generators to a single thread 00622 static random_device rd; 00623 static mt19937 gen(rd()); 00624 normal_distribution<> d(1., resolution); 00625 const double fsmear = max(d(gen), 0.); 00626 const double mass = t.mass2() > 0 ? t.mass() : 0; //< numerical carefulness... 00627 return Particle(t.pid(), FourMomentum::mkXYZM(t.px()*fsmear, t.py()*fsmear, t.pz()*fsmear, mass)); 00628 } 00629 00630 00631 /// ATLAS Run 2 tau smearing 00632 /// @todo Currently a copy of the Run 1 version 00633 inline Particle TAU_SMEAR_ATLAS_RUN2(const Particle& t) { 00634 return TAU_SMEAR_ATLAS_RUN1(t); 00635 } 00636 00637 00638 /// CMS Run 2 tau efficiency 00639 /// 00640 /// @todo Needs work; this is the dumb version from Delphes 3.3.2 00641 inline double TAU_EFF_CMS_RUN2(const Particle& t) { 00642 return (t.abspid() == PID::TAU) ? 0.6 : 0; 00643 } 00644 00645 /// CMS Run 1 tau efficiency 00646 /// 00647 /// @todo Needs work; this is just a copy of the Run 2 version in Delphes 3.3.2 00648 inline double TAU_EFF_CMS_RUN1(const Particle& t) { 00649 return TAU_EFF_CMS_RUN2(t); 00650 } 00651 00652 00653 /// CMS Run 1 tau smearing 00654 /// @todo Currently a copy of the crappy ATLAS one 00655 inline Particle TAU_SMEAR_CMS_RUN1(const Particle& t) { 00656 return TAU_SMEAR_ATLAS_RUN1(t); 00657 } 00658 00659 00660 /// CMS Run 2 tau smearing 00661 /// @todo Currently a copy of the Run 1 version 00662 inline Particle TAU_SMEAR_CMS_RUN2(const Particle& t) { 00663 return TAU_SMEAR_CMS_RUN1(t); 00664 } 00665 00666 //@} 00667 00668 00669 /// @name Jet efficiency and smearing functions 00670 //@{ 00671 00672 /// Return a constant 0 given a Jet as argument 00673 inline double JET_EFF_ZERO(const Jet& p) { return 0; } 00674 /// Return a constant 1 given a Jet as argument 00675 inline double JET_EFF_ONE(const Jet& p) { return 1; } 00676 00677 /// Return 1 if the given Jet contains a b, otherwise 0 00678 inline double JET_BTAG_PERFECT(const Jet& j) { return j.bTagged() ? 1 : 0; } 00679 /// Return the ATLAS Run 1 jet flavour tagging efficiency for the given Jet 00680 inline double JET_BTAG_ATLAS_RUN1(const Jet& j) { 00681 if (j.bTagged()) return 0.80*tanh(0.003*j.pT()/GeV)*(30/(1+0.086*j.pT()/GeV)); 00682 if (j.cTagged()) return 0.20*tanh(0.02*j.pT()/GeV)*(1/(1+0.0034*j.pT()/GeV)); 00683 return 0.002 + 7.3e-6*j.pT()/GeV; 00684 } 00685 /// Return the ATLAS Run 2 MC2c20 jet flavour tagging efficiency for the given Jet 00686 inline double JET_BTAG_ATLAS_RUN2_MV2C20(const Jet& j) { 00687 if (j.bTagged()) return 0.77; 00688 if (j.cTagged()) return 1/4.5; 00689 return 1/140.; 00690 } 00691 /// Return the ATLAS Run 2 MC2c10 jet flavour tagging efficiency for the given Jet 00692 inline double JET_BTAG_ATLAS_RUN2_MV2C10(const Jet& j) { 00693 if (j.bTagged()) return 0.77; 00694 if (j.cTagged()) return 1/6.0; 00695 return 1/134.; 00696 } 00697 00698 /// Return 1 if the given Jet contains a c, otherwise 0 00699 inline double JET_CTAG_PERFECT(const Jet& j) { return j.cTagged() ? 1 : 0; } 00700 00701 /// Take a jet and return an unmodified copy 00702 /// @todo Modify constituent particle vectors for consistency 00703 /// @todo Set a null PseudoJet if the Jet is smeared? 00704 inline Jet JET_SMEAR_IDENTITY(const Jet& j) { return j; } 00705 00706 /// ATLAS Run 1 jet smearing 00707 /// @todo This is a cluster-level flat 3% resolution, I think, and smearing is suboptimal: improve! 00708 inline Jet JET_SMEAR_ATLAS_RUN1(const Jet& j) { 00709 // Const fractional resolution for now 00710 static const double resolution = 0.03; 00711 00712 // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution 00713 /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction? 00714 /// @todo Need to isolate random generators to a single thread 00715 static random_device rd; 00716 static mt19937 gen(rd()); 00717 normal_distribution<> d(1., resolution); 00718 const double fsmear = max(d(gen), 0.); 00719 const double mass = j.mass2() > 0 ? j.mass() : 0; //< numerical carefulness... 00720 return Jet(FourMomentum::mkXYZM(j.px()*fsmear, j.py()*fsmear, j.pz()*fsmear, mass)); 00721 } 00722 00723 /// ATLAS Run 2 jet smearing 00724 /// @todo Just a copy of the Run 1 one: improve!! 00725 inline Jet JET_SMEAR_ATLAS_RUN2(const Jet& j) { 00726 return JET_SMEAR_ATLAS_RUN1(j); 00727 } 00728 00729 /// CMS Run 2 jet smearing 00730 /// @todo Just a copy of the suboptimal ATLAS one: improve!! 00731 inline Jet JET_SMEAR_CMS_RUN2(const Jet& j) { 00732 return JET_SMEAR_ATLAS_RUN1(j); 00733 } 00734 00735 //@} 00736 00737 00738 /// @name ETmiss smearing functions 00739 //@{ 00740 00741 inline Vector3 MET_SMEAR_IDENTITY(const Vector3& met, double) { return met; } 00742 00743 /// @brief ATLAS Run 1 ETmiss smearing 00744 /// 00745 /// Based on https://arxiv.org/pdf/1108.5602v2.pdf, Figs 14 and 15 00746 inline Vector3 MET_SMEAR_ATLAS_RUN1(const Vector3& met, double set) { 00747 // Linearity offset (Fig 14) 00748 Vector3 smeared_met = met; 00749 if (met.mod()/GeV < 25*GeV) smeared_met *= 1.05; 00750 else if (met.mod()/GeV < 40*GeV) smeared_met *= (1.05 - (0.04/15)*(met.mod()/GeV - 25)); //< linear decrease 00751 else smeared_met *= 1.01; 00752 00753 // Smear by a Gaussian with width given by the resolution(sumEt) ~ 0.45 sqrt(sumEt) GeV 00754 const double resolution = 0.45 * sqrt(set/GeV) * GeV; 00755 static random_device rd; 00756 static mt19937 gen(rd()); 00757 normal_distribution<> d(smeared_met.mod(), resolution); 00758 const double metsmear = max(d(gen), 0.); 00759 smeared_met = metsmear * smeared_met.unit(); 00760 00761 return smeared_met; 00762 } 00763 00764 /// ATLAS Run 2 ETmiss smearing 00765 /// @todo Just a copy of the Run 1 one: improve!! 00766 inline Vector3 MET_SMEAR_ATLAS_RUN2(const Vector3& met, double set) { 00767 return MET_SMEAR_ATLAS_RUN1(met, set); 00768 } 00769 00770 /// CMS Run 1 ETmiss smearing 00771 /// @todo Just a copy of the ATLAS one: improve!! 00772 inline Vector3 MET_SMEAR_CMS_RUN1(const Vector3& met, double set) { 00773 return MET_SMEAR_ATLAS_RUN1(met, set); 00774 } 00775 00776 /// CMS Run 2 ETmiss smearing 00777 /// @todo Just a copy of the ATLAS one: improve!! 00778 inline Vector3 MET_SMEAR_CMS_RUN2(const Vector3& met, double set) { 00779 return MET_SMEAR_ATLAS_RUN2(met, set); 00780 } 00781 00782 //@} 00783 00784 00785 } 00786 00787 #endif Generated on Tue Dec 13 2016 16:32:40 for The Rivet MC analysis system by ![]() |