rivet is hosted by Hepforge, IPPP Durham
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