Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:06:51

0001 // -*- C++ -*-
0002 #ifndef RIVET_ExptSmearingFunctions_HH
0003 #define RIVET_ExptSmearingFunctions_HH
0004 
0005 #include "Rivet/Tools/MomentumSmearingFunctions.hh"
0006 #include "Rivet/Tools/ParticleSmearingFunctions.hh"
0007 #include "Rivet/Tools/JetSmearingFunctions.hh"
0008 
0009 namespace Rivet {
0010 
0011 
0012   /// @defgroup smearing Detector smearing & efficiency functions
0013   /// @{
0014 
0015   /// @defgroup smearing_elec Experiment-specific electron efficiency and smearing functions
0016   /// @{
0017 
0018   /// ATLAS Run 1 electron reconstruction efficiency
0019   /// @todo Include reco eff (but no e/y discrimination) in forward region
0020   inline double ELECTRON_RECOEFF_ATLAS_RUN1(const Particle& e) {
0021     if (e.abspid() != PID::ELECTRON) return 0;
0022     if (e.abseta() > 2.5) return 0;
0023     if (e.pT() < 10*GeV) return 0;
0024     return (e.abseta() < 1.5) ? 0.95 : 0.85;
0025   }
0026 
0027   /// ATLAS Run 2 electron reco efficiency
0028   ///
0029   /// Based on https://arxiv.org/pdf/1902.04655.pdf Fig 2
0030   inline double ELECTRON_RECOEFF_ATLAS_RUN2(const Particle& e) {
0031     if (e.abspid() != PID::ELECTRON) return 0;
0032     const double et = e.Et();
0033     if (e.abseta() > 2.5 || e.Et() < 2*GeV) return 0;
0034     if (et > 25*GeV) return 0.97;
0035     if (et > 10*GeV) return 0.92 + (et/GeV-10)/15.*0.05;
0036     if (et >  6*GeV) return 0.85 + (et/GeV-6)/4.*0.07;
0037     if (et >  5*GeV) return 0.70 + (et/GeV-5)/1.*0.15;
0038     if (et >  2*GeV) return 0.00 + (et/GeV-2)/3.*0.70;
0039     return 0;
0040   }
0041 
0042 
0043   /// @brief ATLAS Run 2 'loose' electron reco+identification efficiency
0044   ///
0045   /// Values read from Fig 3 of ATL-PHYS-PUB-2015-041
0046   /// @todo What about faking by jets or non-electrons?
0047   inline double ELECTRON_EFF_ATLAS_RUN2_LOOSE(const Particle& e) {
0048     if (e.abspid() != PID::ELECTRON) return 0;
0049 
0050     // Manually symmetrised eta eff histogram
0051     const static vector<double> edges_eta = { 0.0,   0.1,   0.8,   1.37,  1.52,  2.01,  2.37,  2.47 };
0052     const static vector<double> effs_eta  = { 0.950, 0.965, 0.955, 0.885, 0.950, 0.935, 0.90 };
0053     // Et eff histogram (10-20 is a guess)
0054     const static vector<double> edges_et = { 0,   10,   20,   25,   30,   35,   40,    45,    50,   60,  80 };
0055     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, 0.98 };//Extra value extrapolated for overflow
0056 
0057     if (e.abseta() > 2.47) return 0.0; // no ID outside the tracker
0058 
0059     const int i_eta = binIndex(e.abseta(), edges_eta);
0060     const int i_et = binIndex(e.Et()/GeV, edges_et, true);
0061     const double eff = effs_et[i_et] * effs_eta[i_eta] / 0.95; //< norm factor as approximate double differential
0062     return min(eff, 1.0) * ELECTRON_RECOEFF_ATLAS_RUN2(e);
0063   }
0064 
0065 
0066   /// @brief ATLAS Run 1 'medium' electron reco+identification efficiency
0067   inline double ELECTRON_EFF_ATLAS_RUN1_MEDIUM(const Particle& e) {
0068     if (e.abspid() != PID::ELECTRON) return 0;
0069 
0070     const static vector<double> eta_edges_10 = {0.000, 0.049, 0.454, 1.107, 1.46, 1.790, 2.277, 2.500};
0071     const static vector<double> eta_vals_10  = {0.730, 0.757, 0.780, 0.771, 0.77, 0.777, 0.778};
0072 
0073     const static vector<double> eta_edges_15 = {0.000, 0.053, 0.456, 1.102, 1.463, 1.783, 2.263, 2.500};
0074     const static vector<double> eta_vals_15  = {0.780, 0.800, 0.819, 0.759, 0.749, 0.813, 0.829};
0075 
0076     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};
0077     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};
0078 
0079     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};
0080     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};
0081 
0082     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};
0083     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};
0084 
0085     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};
0086     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};
0087 
0088     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};
0089     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};
0090 
0091     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};
0092     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};
0093 
0094     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};
0095     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};
0096 
0097     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};
0098     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};
0099 
0100     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};
0101     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};
0102 
0103     const static vector<double> et_edges = { 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 80 };
0104     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 };
0105     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 };
0106 
0107     if (e.abseta() > 2.5 || e.Et() < 10*GeV) return 0.0;
0108     const int i_et = binIndex(e.Et()/GeV, et_edges, true);
0109     const int i_eta = binIndex(e.abseta(), et_eta_edges[i_et]);
0110     return et_eta_vals[i_et][i_eta] * ELECTRON_RECOEFF_ATLAS_RUN1(e);
0111   }
0112 
0113   /// @brief ATLAS Run 2 'medium' electron reco+identification efficiency
0114   ///
0115   /// ~1% increase over Run 1 informed by Fig 1 in https://cds.cern.ch/record/2157687/files/ATLAS-CONF-2016-024.pdf
0116   inline double ELECTRON_EFF_ATLAS_RUN2_MEDIUM(const Particle& e) {
0117     if (e.abspid() != PID::ELECTRON) return 0;
0118     return 1.01 * ELECTRON_EFF_ATLAS_RUN1_MEDIUM(e);
0119   }
0120 
0121 
0122   /// @brief ATLAS Run 1 'tight' electron reco+identification efficiency
0123   inline double ELECTRON_EFF_ATLAS_RUN1_TIGHT(const Particle& e) {
0124     if (e.abspid() != PID::ELECTRON) return 0;
0125 
0126     const static vector<double> eta_edges_10 = {0.000, 0.049, 0.459, 1.100, 1.461, 1.789, 2.270, 2.500};
0127     const static vector<double> eta_vals_10  = {0.581, 0.632, 0.668, 0.558, 0.548, 0.662, 0.690};
0128 
0129     const static vector<double> eta_edges_15 = {0.000, 0.053, 0.450, 1.096, 1.463, 1.783, 2.269, 2.500};
0130     const static vector<double> eta_vals_15 =  {0.630, 0.678, 0.714, 0.633, 0.616, 0.700, 0.733};
0131 
0132     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};
0133     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};
0134 
0135     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};
0136     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};
0137 
0138     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};
0139     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};
0140 
0141     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};
0142     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};
0143 
0144     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};
0145     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};
0146 
0147     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};
0148     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};
0149 
0150     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};
0151     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};
0152 
0153     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};
0154     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};
0155 
0156     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};
0157     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};
0158 
0159     const static vector<double> et_edges = { 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 80 };
0160     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 };
0161     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 };
0162 
0163     if (e.abseta() > 2.5 || e.Et() < 10*GeV) return 0.0;
0164     const int i_et = binIndex(e.Et()/GeV, et_edges, true);
0165     const int i_eta = binIndex(e.abseta(), et_eta_edges[i_et]);
0166     return et_eta_vals[i_et][i_eta] * ELECTRON_RECOEFF_ATLAS_RUN1(e);
0167   }
0168 
0169   /// @brief ATLAS Run 2 'tight' electron reco+identification efficiency
0170   ///
0171   /// ~1% increase over Run 1 informed by Fig 1 in https://cds.cern.ch/record/2157687/files/ATLAS-CONF-2016-024.pdf
0172   inline double ELECTRON_EFF_ATLAS_RUN2_TIGHT(const Particle& e) {
0173     if (e.abspid() != PID::ELECTRON) return 0;
0174     const static vector<double> et_edges = { /* 10, 15, */ 20, 25, 30, 35, 40, 45, 50, 60, 80 };
0175     const static vector<double> et_effs = { 0.785, 0.805, 0.820, 0.830, 0.840, 0.850, 0.875, 0.910, 0.910 }; //Extra value extrapolated for overflow
0176     const static vector<double> eta_edges = {0.000, 0.051, 0.374, 0.720, 0.981, 1.279, 1.468, 1.707, 1.945, 2.207, 2.457, 2.500}; // from ET > 80 bin
0177     const static vector<double> eta_refs =    {0.819, 0.855, 0.899, 0.906, 0.900, 0.869, 0.865, 0.873, 0.869, 0.868, 0.859};
0178     if (e.abseta() > 2.5 || e.Et() < 20*GeV) return 0.0;
0179     const int i_et = binIndex(e.Et()/GeV, et_edges, true);
0180     const int i_eta = binIndex(e.abseta(), eta_edges);
0181     const double eff_et = et_effs[i_et]; //< integral eff
0182     // Scale to |eta| shape, following the ~85% efficient high-ET bin from Run 1
0183     const double eff = eff_et * (eta_refs[i_eta]/0.85) * ELECTRON_RECOEFF_ATLAS_RUN2(e);
0184     //return ELECTRON_IDEFF_ATLAS_RUN1_TIGHT(e);
0185     return eff;
0186   }
0187 
0188 
0189 
0190   /// ATLAS Run 1 electron reco smearing
0191   inline Particle ELECTRON_SMEAR_ATLAS_RUN1(const Particle& e) {
0192     static const vector<double> edges_eta = {0., 2.5, 3.};
0193     static const vector<double> edges_pt = {0., 0.1, 25.};
0194     static const vector<double> e2s = {0.000, 0.015, 0.005,
0195                                        0.005, 0.005, 0.005,
0196                                        0.107, 0.107, 0.107};
0197     static const vector<double> es = {0.00, 0.00, 0.05,
0198                                       0.05, 0.05, 0.05,
0199                                       2.08, 2.08, 2.08};
0200     static const vector<double> cs = {0.00, 0.00, 0.25,
0201                                       0.25, 0.25, 0.25,
0202                                       0.00, 0.00, 0.00};
0203 
0204     const int i_eta = binIndex(e.abseta(), edges_eta, true);
0205     const int i_pt = binIndex(e.pT()/GeV, edges_pt, true);
0206     const int i = i_eta*edges_pt.size() + i_pt;
0207 
0208     // Calculate absolute resolution in GeV
0209     const double c1 = sqr(e2s[i]), c2 = sqr(es[i]), c3 = sqr(cs[i]);
0210     const double resolution = sqrt(c1*e.E2() + c2*e.E() + c3) * GeV;
0211 
0212     // normal_distribution<> d(e.E(), resolution);
0213     // const double mass = e.mass2() > 0 ? e.mass() : 0; //< numerical carefulness...
0214     // const double smeared_E = max(d(gen), mass); //< can't let the energy go below the mass!
0215     // return Particle(e.pid(), FourMomentum::mkEtaPhiME(e.eta(), e.phi(), mass, smeared_E));
0216     return Particle(e.pid(), P4_SMEAR_E_GAUSS(e, resolution));
0217   }
0218 
0219 
0220   /// ATLAS Run 2 electron reco smearing
0221   /// @todo Currently just a copy of the Run 1 version: fix!
0222   inline Particle ELECTRON_SMEAR_ATLAS_RUN2(const Particle& e) {
0223     return ELECTRON_SMEAR_ATLAS_RUN1(e);
0224   }
0225 
0226 
0227   /// @todo Add charge flip efficiency?
0228 
0229 
0230 
0231   /// CMS Run 1 electron reconstruction efficiency
0232   inline double ELECTRON_EFF_CMS_RUN1(const Particle& e) {
0233     if (e.abspid() != PID::ELECTRON) return 0;
0234     if (e.abseta() > 2.5) return 0;
0235     if (e.pT() < 10*GeV) return 0;
0236     return (e.abseta() < 1.5) ? 0.95 : 0.85;
0237   }
0238 
0239 
0240   /// CMS Run 2 electron reco efficiency
0241   /// @todo Currently just a copy of Run 1: fix!
0242   inline double ELECTRON_EFF_CMS_RUN2(const Particle& e) {
0243     if (e.abspid() != PID::ELECTRON) return 0;
0244     return ELECTRON_EFF_CMS_RUN1(e);
0245   }
0246 
0247 
0248   /// @brief CMS electron energy smearing, preserving direction
0249   ///
0250   /// Calculate resolution
0251   /// for pT > 0.1 GeV, E resolution = |eta| < 0.5 -> sqrt(0.06^2 + pt^2 * 1.3e-3^2)
0252   ///                                  |eta| < 1.5 -> sqrt(0.10^2 + pt^2 * 1.7e-3^2)
0253   ///                                  |eta| < 2.5 -> sqrt(0.25^2 + pt^2 * 3.1e-3^2)
0254   inline Particle ELECTRON_SMEAR_CMS_RUN1(const Particle& e) {
0255     // Calculate absolute resolution in GeV from functional form
0256     double resolution = 0;
0257     const double abseta = e.abseta();
0258     if (e.pT() > 0.1*GeV && abseta < 2.5) { //< should be a given from efficiencies
0259       if (abseta < 0.5) {
0260         resolution = add_quad(0.06, 1.3e-3 * e.pT()/GeV) * GeV;
0261       } else if (abseta < 1.5) {
0262         resolution = add_quad(0.10, 1.7e-3 * e.pT()/GeV) * GeV;
0263       } else { // still |eta| < 2.5
0264         resolution = add_quad(0.25, 3.1e-3 * e.pT()/GeV) * GeV;
0265       }
0266     }
0267 
0268     // normal_distribution<> d(e.E(), resolution);
0269     // const double mass = e.mass2() > 0 ? e.mass() : 0; //< numerical carefulness...
0270     // const double smeared_E = max(d(gen), mass); //< can't let the energy go below the mass!
0271     // return Particle(e.pid(), FourMomentum::mkEtaPhiME(e.eta(), e.phi(), mass, smeared_E));
0272     return Particle(e.pid(), P4_SMEAR_E_GAUSS(e, resolution));
0273   }
0274 
0275   /// CMS Run 2 electron reco smearing
0276   /// @todo Currently just a copy of the Run 1 version: fix!
0277   inline Particle ELECTRON_SMEAR_CMS_RUN2(const Particle& e) {
0278     return ELECTRON_SMEAR_CMS_RUN1(e);
0279   }
0280 
0281   /// @}
0282 
0283 
0284 
0285   /// @defgroup smearing_photon Experiment-specific photon efficiency and smearing functions
0286   /// @{
0287 
0288   /// @brief ATLAS Run 2 photon reco efficiency
0289   ///
0290   /// Taken from converted photons, Fig 8, in arXiv:1606.01813
0291   inline double PHOTON_EFF_ATLAS_RUN1(const Particle& y) {
0292     if (y.abspid() != PID::PHOTON) return 0; ///< @todo Allow electron misID? What about jet misID?
0293 
0294     if (y.pT() < 10*GeV) return 0;
0295     if (inRange(y.abseta(), 1.37, 1.52) || y.abseta() > 2.37) return 0;
0296 
0297     static const vector<double> edges_eta = {0., 0.6, 1.37, 1.52, 1.81, 2.37};
0298     static const vector<double> edges_pt = {10., 15., 20., 25., 30., 35., 40., 45.,
0299                                             50., 60., 80., 100., 125., 150., 175., 250.};
0300     static const vector<double> effs = {0.53, 0.65, 0.73, 0.83, 0.86, 0.93, 0.94, 0.96,
0301                                         0.97, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98,//
0302                                         0.45, 0.57, 0.67, 0.74, 0.84, 0.87, 0.93, 0.94,
0303                                         0.95, 0.96, 0.97, 0.98, 0.98, 0.99, 0.99, 0.99,//
0304                                         0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
0305                                         0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,//
0306                                         0.48, 0.56, 0.68, 0.76, 0.86, 0.90, 0.93, 0.95,
0307                                         0.96, 0.97, 0.98, 0.99, 0.99, 1.00, 1.00, 1.00,//
0308                                         0.50, 0.61, 0.74, 0.82, 0.88, 0.92, 0.94, 0.95,
0309                                         0.96, 0.97, 0.98, 0.98, 0.98, 0.98, 0.99, 0.99};
0310 
0311     const int i_eta = binIndex(y.abseta(), edges_eta);
0312     const int i_pt = binIndex(y.pT()/GeV, edges_pt, true);
0313     const int i = i_eta*edges_pt.size() + i_pt;
0314     const double eff = effs[i];
0315     return eff;
0316   }
0317 
0318   /// @brief ATLAS Run 2 photon reco efficiency
0319   ///
0320   /// Taken from converted photons, Fig 6, in ATL-PHYS-PUB-2016-014
0321   inline double PHOTON_EFF_ATLAS_RUN2(const Particle& y) {
0322     if (y.abspid() != PID::PHOTON) return 0; ///< @todo Allow electron misID? What about jet misID?
0323 
0324     if (y.pT() < 10*GeV) return 0;
0325     if (inRange(y.abseta(), 1.37, 1.52) || y.abseta() > 2.37) return 0;
0326 
0327     static const vector<double> edges_eta = {0., 0.6, 1.37, 1.52, 1.81, 2.37};
0328     static const vector<double> edges_pt = {10., 15., 20., 25., 30., 35., 40., 45.,
0329                                             50., 60., 80., 100., 125., 150., 175., 250.};
0330     static const vector<double> effs = {0.55, 0.70, 0.85, 0.89, 0.93, 0.95, 0.96, 0.96,
0331                                         0.97, 0.97, 0.98, 0.97, 0.97, 0.97, 0.97, 0.97,//
0332                                         0.47, 0.66, 0.79, 0.86, 0.89, 0.94, 0.96, 0.97,
0333                                         0.97, 0.98, 0.97, 0.98, 0.98, 0.98, 0.98, 0.98,//
0334                                         0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
0335                                         0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,//
0336                                         0.54, 0.71, 0.84, 0.88, 0.92, 0.93, 0.94, 0.95,
0337                                         0.96, 0.96, 0.96, 0.96, 0.96, 0.96, 0.96, 0.96,//
0338                                         0.61, 0.74, 0.83, 0.88, 0.91, 0.94, 0.95, 0.96,
0339                                         0.97, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98, 0.98};
0340 
0341     const int i_eta = binIndex(y.abseta(), edges_eta);
0342     const int i_pt = binIndex(y.pT()/GeV, edges_pt, true);
0343     const int i = i_eta*edges_pt.size() + i_pt;
0344     const double eff = effs[i];
0345     return eff;
0346   }
0347 
0348   /// CMS Run 1 photon reco efficiency
0349   /// @todo Currently from Delphes
0350   inline double PHOTON_EFF_CMS_RUN1(const Particle& y) {
0351     if (y.abspid() != PID::PHOTON) return 0; ///< @todo Allow electron misID? What about jet misID?
0352     if (y.pT() < 10*GeV || y.abseta() > 2.5) return 0;
0353     return (y.abseta() < 1.5) ? 0.95 : 0.85;
0354   }
0355 
0356   /// CMS Run 2 photon reco efficiency
0357   /// @todo Currently just a copy of Run 1: fix!
0358   inline double PHOTON_EFF_CMS_RUN2(const Particle& y) {
0359     if (y.abspid() != PID::PHOTON) return 0; ///< @todo Allow electron misID? What about jet misID?
0360     return PHOTON_EFF_CMS_RUN1(y);
0361   }
0362 
0363 
0364   /// @todo Use real photon smearing
0365   inline Particle PHOTON_SMEAR_ATLAS_RUN1(const Particle& y) { return y; }
0366   inline Particle PHOTON_SMEAR_ATLAS_RUN2(const Particle& y) { return y; }
0367   inline Particle PHOTON_SMEAR_CMS_RUN1(const Particle& y) { return y; }
0368   inline Particle PHOTON_SMEAR_CMS_RUN2(const Particle& y) { return y; }
0369 
0370   /// @}
0371 
0372 
0373 
0374   /// @defgroup smearing_muon Experiment-specific muon efficiency and smearing functions
0375   /// @{
0376 
0377   /// ATLAS Run 1 muon reco efficiency
0378   inline double MUON_EFF_ATLAS_RUN1(const Particle& m) {
0379     if (m.abspid() != PID::MUON) return 0;
0380     if (m.abseta() > 2.7) return 0;
0381     if (m.pT() < 10*GeV) return 0;
0382     return (m.abseta() < 1.5) ? 0.95 : 0.85;
0383   }
0384 
0385   /// ATLAS Run 2 muon reco efficiency
0386   ///
0387   /// From https://arxiv.org/pdf/1603.05598.pdf , Fig3 top
0388   inline double MUON_RECOEFF_ATLAS_RUN2(const Particle& m) {
0389     if (m.abspid() != PID::MUON) return 0;
0390     if (m.abseta() > 2.5) return 0;
0391     if (m.abseta() < 0.1) return 0.61;
0392     // if (m.pT() < 10*GeV) return 0;
0393     return (m.abseta() < 1) ? 0.98 : 0.99;
0394   }
0395 
0396   /// @brief ATLAS Run 2 muon reco+ID efficiency
0397   ///
0398   /// For medium ID, from Fig 3 of
0399   /// https://cds.cern.ch/record/2047831/files/ATL-PHYS-PUB-2015-037.pdf
0400   inline double MUON_EFF_ATLAS_RUN2(const Particle& m) {
0401     if (m.abspid() != PID::MUON) return 0;
0402     if (m.abseta() > 2.7) return 0;
0403     static const vector<double> edges_pt = {0., 3.5, 4., 5., 6., 7., 8., 10.};
0404     static const vector<double> effs = {0.00, 0.76, 0.94, 0.97, 0.98, 0.98, 0.98, 0.99};
0405     const int i_pt = binIndex(m.pT()/GeV, edges_pt, true);
0406     const double eff = effs[i_pt] * MUON_RECOEFF_ATLAS_RUN2(m);
0407     return eff;
0408   }
0409 
0410   /// @todo Add muon loose/medium/tight ID efficiencies? All around 95-98%... ignore?
0411 
0412 
0413 
0414   /// ATLAS Run 1 muon reco smearing
0415   inline Particle MUON_SMEAR_ATLAS_RUN1(const Particle& m) {
0416     static const vector<double> edges_eta = {0, 1.5, 2.5};
0417     static const vector<double> edges_pt = {0, 0.1, 1.0, 10., 200.};
0418     static const vector<double> res = {0., 0.03, 0.02, 0.03, 0.05,
0419                                        0., 0.04, 0.03, 0.04, 0.05};
0420 
0421     const int i_eta = binIndex(m.abseta(), edges_eta);
0422     const int i_pt = binIndex(m.pT()/GeV, edges_pt, true);
0423     const int i = i_eta*edges_pt.size() + i_pt;
0424 
0425     const double resolution = res[i];
0426 
0427     // Smear by a Gaussian centered on the current pT, with width given by the resolution
0428     // normal_distribution<> d(m.pT(), resolution*m.pT());
0429     // const double smeared_pt = max(d(gen), 0.);
0430     // const double mass = m.mass2() > 0 ? m.mass() : 0; //< numerical carefulness...
0431     // return Particle(m.pid(), FourMomentum::mkEtaPhiMPt(m.eta(), m.phi(), mass, smeared_pt));
0432     return Particle(m.pid(), P4_SMEAR_PT_GAUSS(m, resolution*m.pT()));
0433   }
0434 
0435   /// ATLAS Run 2 muon reco smearing
0436   /// From https://arxiv.org/abs/1603.05598 , eq (10) and Fig 12
0437   inline Particle MUON_SMEAR_ATLAS_RUN2(const Particle& m) {
0438     double mres_pt = 0.015;
0439     if (m.pT() > 50*GeV) mres_pt = 0.014 + 0.01*(m.pT()/GeV-50)/50;
0440     if (m.pT() > 100*GeV) mres_pt = 0.025;
0441     const double ptres_pt = SQRT2 * mres_pt; //< from Eq (10)
0442     const double resolution = (m.abseta() < 1.5 ? 1.0 : 1.25) * ptres_pt;
0443     return Particle(m.pid(), P4_SMEAR_PT_GAUSS(m, resolution*m.pT()));
0444   }
0445 
0446 
0447 
0448   /// CMS Run 1 muon reco efficiency
0449   inline double MUON_EFF_CMS_RUN1(const Particle& m) {
0450     if (m.abspid() != PID::MUON) return 0;
0451     if (m.abseta() > 2.4) return 0;
0452     if (m.pT() < 10*GeV) return 0;
0453     return 0.95 * (m.abseta() < 1.5 ? 1 : exp(0.5 - 5e-4*m.pT()/GeV));
0454   }
0455 
0456   /// CMS Run 2 muon reco efficiency
0457   /// @todo Currently just a copy of Run 1: fix!
0458   inline double MUON_EFF_CMS_RUN2(const Particle& m) {
0459     if (m.abspid() != PID::MUON) return 0;
0460     return MUON_EFF_CMS_RUN1(m);
0461   }
0462 
0463 
0464   /// CMS Run 1 muon reco smearing
0465   inline Particle MUON_SMEAR_CMS_RUN1(const Particle& m) {
0466     // Calculate fractional resolution
0467     // for pT > 0.1 GeV, mom resolution = |eta| < 0.5 -> sqrt(0.01^2 + pt^2 * 2.0e-4^2)
0468     //                                    |eta| < 1.5 -> sqrt(0.02^2 + pt^2 * 3.0e-4^2)
0469     //                                    |eta| < 2.5 -> sqrt(0.05^2 + pt^2 * 2.6e-4^2)
0470     double resolution = 0;
0471     const double abseta = m.abseta();
0472     if (m.pT() > 0.1*GeV && abseta < 2.5) {
0473       if (abseta < 0.5) {
0474         resolution = add_quad(0.01, 2.0e-4 * m.pT()/GeV);
0475       } else if (abseta < 1.5) {
0476         resolution = add_quad(0.02, 3.0e-4 * m.pT()/GeV);
0477       } else { // still |eta| < 2.5... but isn't CMS' mu acceptance < 2.4?
0478         resolution = add_quad(0.05, 2.6e-4 * m.pT()/GeV);
0479       }
0480     }
0481 
0482     // Smear by a Gaussian centered on the current pT, with width given by the resolution
0483     // normal_distribution<> d(m.pT(), resolution*m.pT());
0484     // const double smeared_pt = max(d(gen), 0.);
0485     // const double mass = m.mass2() > 0 ? m.mass() : 0; //< numerical carefulness...
0486     // return Particle(m.pid(), FourMomentum::mkEtaPhiMPt(m.eta(), m.phi(), mass, smeared_pt));
0487     return Particle(m.pid(), P4_SMEAR_PT_GAUSS(m, resolution*m.pT()));
0488   }
0489 
0490   /// CMS Run 2 muon reco smearing
0491   /// @todo Currently just a copy of the Run 1 version: fix!
0492   inline Particle MUON_SMEAR_CMS_RUN2(const Particle& m) {
0493     return MUON_SMEAR_CMS_RUN1(m);
0494   }
0495 
0496   /// @}
0497 
0498 
0499 
0500   /// @defgroup smearing_tau Experiment-specific tau efficiency and smearing functions
0501   /// @{
0502 
0503   /// @brief ATLAS Run 1 8 TeV tau efficiencies (medium working point)
0504   ///
0505   /// Taken from http://arxiv.org/pdf/1412.7086.pdf
0506   ///   20-40 GeV 1-prong LMT eff|mis = 0.66|1/10, 0.56|1/20, 0.36|1/80
0507   ///   20-40 GeV 3-prong LMT eff|mis = 0.45|1/60, 0.38|1/100, 0.27|1/300
0508   ///   > 40 GeV 1-prong LMT eff|mis = 0.66|1/15, 0.56|1/25, 0.36|1/80
0509   ///   > 40 GeV 3-prong LMT eff|mis = 0.45|1/250, 0.38|1/400, 0.27|1/1300
0510   inline double TAU_EFF_ATLAS_RUN1(const Particle& t) {
0511     if (t.abseta() > 2.5) return 0; //< hmm... mostly
0512     if (inRange(t.abseta(), 1.37, 1.52)) return 0; //< crack region
0513     double pThadvis = 0;
0514     Particles chargedhadrons;
0515     for (const Particle& p : t.children()) {
0516       if (p.isHadron()) {
0517         pThadvis += p.pT(); //< right definition? Paper is unclear
0518         if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p;
0519       }
0520     }
0521     if (chargedhadrons.empty()) return 0; //< leptonic tau
0522     if (pThadvis < 20*GeV) return 0; //< below threshold
0523     if (pThadvis < 40*GeV) {
0524       if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.56 : 0; //1/20.;
0525       if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.38 : 0; //1/100.;
0526     } else {
0527       if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.56 : 0; //1/25.;
0528       if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.38 : 0; //1/400.;
0529     }
0530     return 0;
0531   }
0532 
0533   /// @brief ATLAS Run 1 8 TeV tau misID rates (medium working point)
0534   ///
0535   /// Taken from http://arxiv.org/pdf/1412.7086.pdf
0536   ///   20-40 GeV 1-prong LMT eff|mis = 0.66|1/10, 0.56|1/20, 0.36|1/80
0537   ///   20-40 GeV 3-prong LMT eff|mis = 0.45|1/60, 0.38|1/100, 0.27|1/300
0538   ///   > 40 GeV 1-prong LMT eff|mis = 0.66|1/15, 0.56|1/25, 0.36|1/80
0539   ///   > 40 GeV 3-prong LMT eff|mis = 0.45|1/250, 0.38|1/400, 0.27|1/1300
0540   inline double TAUJET_EFF_ATLAS_RUN1(const Jet& j) {
0541     if (j.abseta() > 2.5) return 0; //< hmm... mostly
0542     if (inRange(j.abseta(), 1.37, 1.52)) return 0; //< crack region
0543     double pThadvis = 0;
0544     Particles chargedhadrons;
0545     for (const Particle& p : j.particles()) {
0546       if (p.isHadron()) {
0547         pThadvis += p.pT(); //< right definition? Paper is unclear
0548         if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p;
0549       }
0550     }
0551 
0552     if (chargedhadrons.empty()) return 0; //< leptonic tau?
0553     if (pThadvis < 20*GeV) return 0; //< below threshold
0554 
0555     const Particles ttags = j.tauTags(Cuts::pT > 10*GeV);
0556     // MisID rates for jets without truth tau label
0557     if (ttags.empty()) {
0558       if (pThadvis < 40*GeV)
0559         return chargedhadrons.size() == 1 ? 1/20. : chargedhadrons.size() == 3 ? 1/100. : 0; //< fake rates
0560       else
0561         return chargedhadrons.size() == 1 ? 1/25. : chargedhadrons.size() == 3 ? 1/400. : 0; //< fake rates
0562     }
0563     // Efficiencies for jets with a truth tau label
0564     const Particles prongs = ttags[0].stableDescendants(Cuts::charge3 > 0 && Cuts::pT > 1*GeV && Cuts::abseta < 2.5);
0565     return prongs.size() == 1 ? 0.56 : 0.38;
0566   }
0567 
0568 
0569   /// @brief ATLAS Run 2 13 TeV tau efficiencies (medium working point)
0570   ///
0571   /// From https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/PUBNOTES/ATL-PHYS-PUB-2015-045/ATL-PHYS-PUB-2015-045.pdf
0572   ///   LMT 1 prong efficiency/mistag = 0.6|1/30, 0.55|1/50, 0.45|1/120
0573   ///   LMT 3 prong efficiency/mistag = 0.5|1/30, 0.4|1/110, 0.3|1/300
0574   inline double TAU_EFF_ATLAS_RUN2(const Particle& t) {
0575     if (t.abspid() != PID::TAU) return 0;
0576     if (t.abseta() > 2.5) return 0; //< hmm... mostly
0577     if (inRange(t.abseta(), 1.37, 1.52)) return 0; //< crack region
0578     double pThadvis = 0;
0579     Particles chargedhadrons;
0580     for (const Particle& p : t.children()) {
0581       if (p.isHadron()) {
0582         pThadvis += p.pT(); //< right definition? Paper is unclear
0583         if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p;
0584       }
0585     }
0586     if (chargedhadrons.empty()) return 0; //< leptonic tau
0587     if (pThadvis < 20*GeV) return 0; //< below threshold
0588     if (chargedhadrons.size() == 1) return (t.abspid() == PID::TAU) ? 0.55 : 0; //1/50.;
0589     if (chargedhadrons.size() == 3) return (t.abspid() == PID::TAU) ? 0.40 : 0; //1/110.;
0590     return 0;
0591   }
0592 
0593   /// @brief ATLAS Run 2 13 TeV tau misID rate (medium working point)
0594   ///
0595   /// From https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/PUBNOTES/ATL-PHYS-PUB-2015-045/ATL-PHYS-PUB-2015-045.pdf
0596   ///   LMT 1 prong efficiency/mistag = 0.6|1/30, 0.55|1/50, 0.45|1/120
0597   ///   LMT 3 prong efficiency/mistag = 0.5|1/30, 0.4|1/110, 0.3|1/300
0598   inline double TAUJET_EFF_ATLAS_RUN2(const Jet& j) {
0599     if (j.abseta() > 2.5) return 0; //< hmm... mostly
0600     if (inRange(j.abseta(), 1.37, 1.52)) return 0; //< crack region
0601     double pThadvis = 0;
0602     Particles chargedhadrons;
0603     for (const Particle& p : j.particles()) {
0604       if (p.isHadron()) {
0605         pThadvis += p.pT(); //< right definition? Paper is unclear
0606         if (p.charge3() != 0 && p.abseta() < 2.5 && p.pT() > 1*GeV) chargedhadrons += p;
0607       }
0608     }
0609     if (chargedhadrons.empty()) return 0;
0610     if (pThadvis < 20*GeV) return 0; //< below threshold
0611     const Particles ttags = j.tauTags(Cuts::pT > 10*GeV);
0612     // if (ttags.empty()) {
0613     //   if (pThadvis < 40*GeV)
0614     //     return chargedhadrons.size() == 1 ? 1/50. : 1/110.; //< fake rates
0615     //   else
0616     //     return chargedhadrons.size() == 1 ? 1/25. : 1/400.; //< fake rates
0617     // }
0618     if (ttags.empty()) return chargedhadrons.size() == 1 ? 1/50. : chargedhadrons.size() == 3 ? 1/110. : 0; //< fake rates
0619     const Particles prongs = ttags[0].stableDescendants(Cuts::charge3 > 0 && Cuts::pT > 1*GeV && Cuts::abseta < 2.5);
0620     return prongs.size() == 1 ? 0.55 : 0.40;
0621   }
0622 
0623 
0624   /// ATLAS Run 1 tau smearing
0625   /// @todo Currently a copy of the jet smearing
0626   inline Particle TAU_SMEAR_ATLAS_RUN1(const Particle& t) {
0627     // // Const fractional resolution for now
0628     // static const double resolution = 0.03;
0629 
0630     // // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution
0631     // /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction?
0632     // const double fsmear = max(randnorm(1., resolution), 0.);
0633     // const double mass = t.mass2() > 0 ? t.mass() : 0; //< numerical carefulness...
0634     // return Particle(t.pid(), FourMomentum::mkXYZM(t.px()*fsmear, t.py()*fsmear, t.pz()*fsmear, mass));
0635 
0636     // Jet energy resolution lookup
0637     //   Implemented by Matthias Danninger for GAMBIT, based roughly on
0638     //   https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/CONFNOTES/ATLAS-CONF-2015-017/
0639     //   Parameterisation can be still improved, but eta dependence is minimal
0640     /// @todo Also need a JES uncertainty component?
0641     static const vector<double> binedges_pt = {0., 50., 70., 100., 150., 200., 1000., 10000.};
0642     static const vector<double> jer = {0.145, 0.115, 0.095, 0.075, 0.07, 0.05, 0.04, 0.04}; //< note overflow value
0643     const int ipt = binIndex(t.pT()/GeV, binedges_pt, true);
0644     if (ipt < 0) return t;
0645     const double resolution = jer.at(ipt);
0646 
0647     // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution
0648     /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction?
0649     const double fsmear = max(randnorm(1., resolution), 0.);
0650     const double mass = t.mass2() > 0 ? t.mass() : 0; //< numerical carefulness...
0651     Particle rtn(PID::TAU, FourMomentum::mkXYZM(t.px()*fsmear, t.py()*fsmear, t.pz()*fsmear, mass));
0652     //if (deltaPhi(t, rtn) > 0.01) cout << "jdphi: " << deltaPhi(t, rtn) << endl;
0653     return rtn;
0654   }
0655 
0656 
0657   /// ATLAS Run 2 tau smearing
0658   /// @todo Currently a copy of the Run 1 version
0659   inline Particle TAU_SMEAR_ATLAS_RUN2(const Particle& t) {
0660     return TAU_SMEAR_ATLAS_RUN1(t);
0661   }
0662 
0663 
0664   /// CMS Run 1 tau efficiency
0665   ///
0666   /// @todo Needs work; this is just a copy of the Run 2 version in Delphes 3.3.2
0667   inline double TAU_EFF_CMS_RUN1(const Particle& t) {
0668     if (t.abspid() != PID::TAU) return 0;
0669     return (t.abspid() == PID::TAU) ? 0.6 : 0;
0670   }
0671 
0672   /// CMS Run 2 tau efficiency
0673   ///
0674   /// @todo Needs work; this is the dumb version from Delphes 3.3.2
0675   inline double TAU_EFF_CMS_RUN2(const Particle& t) {
0676     if (t.abspid() != PID::TAU) return 0;
0677     return (t.abspid() == PID::TAU) ? 0.6 : 0;
0678   }
0679 
0680 
0681   /// CMS Run 1 tau smearing
0682   /// @todo Currently a copy of the crappy ATLAS one
0683   inline Particle TAU_SMEAR_CMS_RUN1(const Particle& t) {
0684     return TAU_SMEAR_ATLAS_RUN1(t);
0685   }
0686 
0687 
0688   /// CMS Run 2 tau smearing
0689   /// @todo Currently a copy of the Run 1 version
0690   inline Particle TAU_SMEAR_CMS_RUN2(const Particle& t) {
0691     return TAU_SMEAR_CMS_RUN1(t);
0692   }
0693 
0694   /// @}
0695 
0696 
0697 
0698   /// @defgroup smearing_jet Experiment-specific jet efficiency and smearing functions
0699   /// @{
0700 
0701   /// Return the ATLAS Run 1 jet flavour tagging efficiency for the given Jet, from Delphes
0702   inline double JET_BTAG_ATLAS_RUN1(const Jet& j) {
0703     /// @todo This form drops past ~100 GeV, asymptotically to zero efficiency... really?!
0704     if (j.abseta() > 2.5) return 0;
0705     const auto ftagsel = [&](const Particle& p){ return p.pT() > 5*GeV && deltaR(p,j) < 0.3; };
0706     if (j.bTagged(ftagsel)) return 0.80*tanh(0.003*j.pT()/GeV)*(30/(1+0.0860*j.pT()/GeV));
0707     if (j.cTagged(ftagsel)) return 0.20*tanh(0.020*j.pT()/GeV)*( 1/(1+0.0034*j.pT()/GeV));
0708     return 0.002 + 7.3e-6*j.pT()/GeV;
0709   }
0710 
0711   /// Return the ATLAS Run 2 MC2c20 77% WP jet flavour tagging efficiency for the given Jet
0712   inline double JET_BTAG_ATLAS_RUN2_MV2C20(const Jet& j) {
0713     if (j.abseta() > 2.5) return 0;
0714     if (j.bTagged(Cuts::pT > 5*GeV)) return 0.77;
0715     if (j.cTagged(Cuts::pT > 5*GeV)) return 1/4.5;
0716     return 1/140.;
0717   }
0718 
0719   /// Return the ATLAS Run 2 MC2c10 77% WP jet flavour tagging efficiency for the given Jet
0720   inline double JET_BTAG_ATLAS_RUN2_MV2C10(const Jet& j) {
0721     if (j.abseta() > 2.5) return 0;
0722     if (j.bTagged(Cuts::pT > 5*GeV)) return 0.77;
0723     if (j.cTagged(Cuts::pT > 5*GeV)) return 1/6.0;
0724     return 1/134.;
0725   }
0726 
0727 
0728   /// ATLAS Run 1 jet smearing
0729   inline Jet JET_SMEAR_ATLAS_RUN1(const Jet& j) {
0730     // Jet energy resolution lookup
0731     //   Implemented by Matthias Danninger for GAMBIT, based roughly on
0732     //   https://atlas.web.cern.ch/Atlas/GROUPS/PHYSICS/CONFNOTES/ATLAS-CONF-2015-017/
0733     //   Parameterisation can be still improved, but eta dependence is minimal
0734     /// @todo Also need a JES uncertainty component?
0735     static const vector<double> binedges_pt = {0., 50., 70., 100., 150., 200., 1000., 10000.};
0736     static const vector<double> jer = {0.145, 0.115, 0.095, 0.075, 0.07, 0.05, 0.04, 0.04}; //< note overflow value
0737     const int ipt = binIndex(j.pT()/GeV, binedges_pt, true);
0738     if (ipt < 0) return j;
0739     const double resolution = jer.at(ipt);
0740 
0741     // Smear by a Gaussian centered on 1 with width given by the (fractional) resolution
0742     /// @todo Is this the best way to smear? Should we preserve the energy, or pT, or direction?
0743     const double fsmear = max(randnorm(1., resolution), 0.);
0744     const double mass = j.mass2() > 0 ? j.mass() : 0; //< numerical carefulness...
0745     Jet rtn(FourMomentum::mkXYZM(j.px()*fsmear, j.py()*fsmear, j.pz()*fsmear, mass));
0746     //if (deltaPhi(j, rtn) > 0.01) cout << "jdphi: " << deltaPhi(j, rtn) << endl;
0747     return rtn;
0748   }
0749 
0750   /// ATLAS Run 2 jet smearing
0751   /// @todo Just a copy of the Run 1 one: improve!!
0752   inline Jet JET_SMEAR_ATLAS_RUN2(const Jet& j) {
0753     return JET_SMEAR_ATLAS_RUN1(j);
0754   }
0755 
0756   /// CMS Run 2 jet smearing
0757   /// @todo Just a copy of the suboptimal ATLAS one: improve!!
0758   inline Jet JET_SMEAR_CMS_RUN1(const Jet& j) {
0759     return JET_SMEAR_ATLAS_RUN1(j);
0760   }
0761 
0762   /// CMS Run 2 jet smearing
0763   /// @todo Just a copy of the suboptimal ATLAS one: improve!!
0764   inline Jet JET_SMEAR_CMS_RUN2(const Jet& j) {
0765     return JET_SMEAR_CMS_RUN1(j);
0766   }
0767 
0768   /// @}
0769 
0770 
0771   /// @defgroup smearing_met Experiment-specific missing-ET smearing functions
0772   /// @{
0773 
0774   inline Vector3 MET_SMEAR_IDENTITY(const Vector3& met, double) { return met; }
0775 
0776   /// @brief ATLAS Run 1 ETmiss smearing
0777   ///
0778   /// Based on https://arxiv.org/pdf/1108.5602v2.pdf, Figs 14 and 15
0779   inline Vector3 MET_SMEAR_ATLAS_RUN1(const Vector3& met, double set) {
0780     Vector3 smeared_met = met;
0781 
0782     // Linearity offset (Fig 14)
0783     if (met.mod() < 25*GeV) smeared_met *= 1.05;
0784     else if (met.mod() < 40*GeV) smeared_met *= (1.05 - (0.04/15)*(met.mod()/GeV - 25)); //< linear decrease
0785     else smeared_met *= 1.01;
0786 
0787     // Smear by a Gaussian with width given by the resolution(sumEt) ~ 0.45 sqrt(sumEt) GeV
0788     const double resolution = 0.45 * sqrt(set/GeV) * GeV;
0789     //const double metsmear = max(randnorm(smeared_met.mod(), resolution), 0.);
0790     const double metsmear = fabs(randnorm(smeared_met.mod(), resolution)); //< better to reflect than to create a peak at 0
0791     smeared_met = metsmear * smeared_met.unit();
0792 
0793     return smeared_met;
0794   }
0795 
0796   /// ATLAS Run 2 ETmiss smearing
0797   ///
0798   /// Based on https://arxiv.org/pdf/1802.08168.pdf, Figs 6-9
0799   inline Vector3 MET_SMEAR_ATLAS_RUN2(const Vector3& met, double set) {
0800     Vector3 smeared_met = met;
0801 
0802     // Linearity offset (Fig 6)
0803     if (met.mod() < 25*GeV) smeared_met *= 1.5;
0804     else smeared_met *= (1 + exp(-(met.mod() - 25*GeV)/(10*GeV)) - 0.02); //< exp approx to Fig 6 curve, approaching -0.02
0805 
0806     // Resolution(sumEt) ~ 0.45 sqrt(sumEt) GeV
0807     // above SET = 180 GeV, and with linear trend from SET = 180 -> 0  to resolution = 0 (Fig 7)
0808     const double resolution1 = (set < 180*GeV ? set/180. : 1) * 0.45 * sqrt(max(set/GeV, 180)) * GeV;
0809     /// @todo Allow smearing function to access the whole event, since Njet also affects? Or assume encoded in SET?
0810 
0811     // Resolution(MET_true) (Fig 9)
0812     const double resolution2 = 15*GeV + 0.5*sqrt(met.mod()/GeV)*GeV;
0813 
0814     // Smear by a Gaussian with width given by the minimum resolution estimator (should mean low-MET events
0815     // with high SET do not get a large smearing, and will be dominated by the linearity effect).
0816     const double resolution = min(resolution1, resolution2);
0817     //const double metsmear = max(randnorm(smeared_met.mod(), resolution), 0.);
0818     const double metsmear = fabs(randnorm(smeared_met.mod(), resolution)); //< better to reflect than to create a peak at 0
0819     smeared_met = metsmear * smeared_met.unit();
0820 
0821     return smeared_met;
0822   }
0823 
0824   /// CMS Run 1 ETmiss smearing
0825   /// From https://arxiv.org/pdf/1411.0511.pdf Table 2, p16 (Z channels)
0826   inline Vector3 MET_SMEAR_CMS_RUN1(const Vector3& met, double set) {
0827     Vector3 smeared_met = met;
0828 
0829     // Calculate parallel and perpendicular resolutions and combine in quadrature (?)
0830     const double resolution_x = (1.1 + 0.6*sqrt(set/GeV)) * GeV;
0831     const double resolution_y = (1.4 + 0.6*sqrt(set/GeV)) * GeV;
0832     const double resolution = sqrt(sqr(resolution_x) + sqr(resolution_y));
0833 
0834     // Smear by a Gaussian with width given by the resolution
0835     // const double metsmear = max(randnorm(smeared_met.mod(), resolution), 0.);
0836     const double metsmear = fabs(randnorm(smeared_met.mod(), resolution)); //< better to reflect than to create a peak at 0
0837     smeared_met = metsmear * smeared_met.unit();
0838 
0839     return smeared_met;
0840   }
0841 
0842   /// CMS Run 2 ETmiss smearing
0843   /// From http://inspirehep.net/record/1681214/files/JME-17-001-pas.pdf Table 3, p20
0844   inline Vector3 MET_SMEAR_CMS_RUN2(const Vector3& met, double set) {
0845     Vector3 smeared_met = met;
0846 
0847     // Calculate parallel and perpendicular resolutions and combine in quadrature (?)
0848     const double resolution_para = ( 2.0 + 0.64*sqrt(set/GeV)) * GeV;
0849     const double resolution_perp = (-1.5 + 0.68*sqrt(set/GeV)) * GeV;
0850     const double resolution = sqrt(sqr(resolution_para) + sqr(resolution_perp));
0851 
0852     // Smear by a Gaussian with width given by the resolution
0853     // const double metsmear = max(randnorm(smeared_met.mod(), resolution), 0.);
0854     const double metsmear = fabs(randnorm(smeared_met.mod(), resolution)); //< better to reflect than to create a peak at 0
0855     smeared_met = metsmear * smeared_met.unit();
0856 
0857     return smeared_met;
0858   }
0859 
0860   /// @}
0861 
0862 
0863   /// @defgroup smearing_trk Experiment-specific tracking efficiency and smearing functions
0864   /// @{
0865 
0866   /// ATLAS Run 1 tracking efficiency
0867   inline double TRK_EFF_ATLAS_RUN1(const Particle& p) {
0868     if (p.charge3() == 0) return 0;
0869     if (p.abseta() > 2.5) return 0;
0870     if (p.pT() < 0.1*GeV) return 0;
0871 
0872     if (p.abspid() == PID::ELECTRON) {
0873       if (p.abseta() < 1.5) {
0874         if (p.pT() < 1*GeV) return 0.73;
0875         if (p.pT() < 100*GeV) return 0.95;
0876         return 0.99;
0877       } else {
0878         if (p.pT() < 1*GeV) return 0.50;
0879         if (p.pT() < 100*GeV) return 0.83;
0880         else return 0.90;
0881       }
0882     } else if (p.abspid() == PID::MUON) {
0883       if (p.abseta() < 1.5) {
0884         return (p.pT() < 1*GeV) ? 0.75 : 0.99;
0885       } else {
0886         return (p.pT() < 1*GeV) ? 0.70 : 0.98;
0887       }
0888     } else { // charged hadrons
0889       if (p.abseta() < 1.5) {
0890         return (p.pT() < 1*GeV) ? 0.70 : 0.95;
0891       } else {
0892         return (p.pT() < 1*GeV) ? 0.60 : 0.85;
0893       }
0894     }
0895   }
0896 
0897   /// ATLAS Run 2 tracking efficiency
0898   /// @todo Currently just a copy of Run 1: fix!
0899   inline double TRK_EFF_ATLAS_RUN2(const Particle& p) {
0900     return TRK_EFF_ATLAS_RUN1(p);
0901   }
0902 
0903 
0904   /// CMS Run 1 tracking efficiency
0905   inline double TRK_EFF_CMS_RUN1(const Particle& p) {
0906     if (p.charge3() == 0) return 0;
0907     if (p.abseta() > 2.5) return 0;
0908     if (p.pT() < 0.1*GeV) return 0;
0909 
0910     if (p.abspid() == PID::ELECTRON) {
0911       if (p.abseta() < 1.5) {
0912         if (p.pT() < 1*GeV) return 0.73;
0913         if (p.pT() < 100*GeV) return 0.95;
0914         return 0.99;
0915       } else {
0916         if (p.pT() < 1*GeV) return 0.50;
0917         if (p.pT() < 100*GeV) return 0.83;
0918         else return 0.90;
0919       }
0920     } else if (p.abspid() == PID::MUON) {
0921       if (p.abseta() < 1.5) {
0922         return (p.pT() < 1*GeV) ? 0.75 : 0.99;
0923       } else {
0924         return (p.pT() < 1*GeV) ? 0.70 : 0.98;
0925       }
0926     } else { // charged hadrons
0927       if (p.abseta() < 1.5) {
0928         return (p.pT() < 1*GeV) ? 0.70 : 0.95;
0929       } else {
0930         return (p.pT() < 1*GeV) ? 0.60 : 0.85;
0931       }
0932     }
0933   }
0934 
0935   /// CMS Run 2 tracking efficiency
0936   /// @todo Currently just a copy of Run 1: fix!
0937   inline double TRK_EFF_CMS_RUN2(const Particle& p) {
0938     return TRK_EFF_CMS_RUN1(p);
0939   }
0940 
0941   /// @brief Return the efficiency of the ATLAS JVT tagger at > 0.2 W.P.
0942   ///
0943   /// Values taken from fig 17, https://arxiv.org/pdf/1510.03823.pdf.
0944   /// Shockingly, its run1, but it's still the most recent ATLAS JVT public plots
0945   /// And is still cited by all the Run 2 papers
0946   /// Note! The exact translation of weak/medium/tight varies across runs/years
0947   /// If possible double check the score cut off used.
0948   inline double ATLAS_JVT_EFF_LOOSE(const Jet & j){
0949     if (j.abseta() > 2.4) return 1.0;
0950     // No data for jets pt < 20, hopefully not relevant:
0951     if (j.pt() <= 20*GeV || j.pt() >= 60*GeV) return 1.0;
0952 
0953     const static vector<double> binedges_pt = {20*GeV,25*GeV,30*GeV,35*GeV,40*GeV,45*GeV,50*GeV,55*GeV,60*GeV};
0954     const static vector<double> binvals = {0.9, 0.93, 0.94, 0.95, 0.96, 0.96, 0.97, 0.97};
0955 
0956     const size_t bini = binIndex(j.pt(), binedges_pt);
0957     return binvals[bini];
0958   }
0959 
0960   /// @brief Return the efficiency of the ATLAS JVT tagger at > 0.4 W.P.
0961   ///
0962   /// Values taken from fig 17, https://arxiv.org/pdf/1510.03823.pdf.
0963   /// Shockingly, its run1, but it's still the most recent ATLAS JVT public plots
0964   /// And is still cited by all the Run 2 papers
0965   /// Note! The exact translation of weak/medium/tight varies across runs/years
0966   /// If possible double check the score cut off used.
0967   inline double ATLAS_JVT_EFF_MEDIUM(const Jet & j){
0968     if (j.abseta() > 2.4) return 1.0;
0969     // No data for jets pt < 20, hopefully not relevant:
0970     if (j.pt() <= 20*GeV || j.pt() >= 60*GeV) return 1.0;
0971 
0972     const static vector<double> binedges_pt = {20*GeV,25*GeV,30*GeV,35*GeV,40*GeV,45*GeV,50*GeV,55*GeV,60*GeV};
0973     const static vector<double> binvals = {0.86, 0.9, 0.92, 0.93, 0.94, 0.95, 0.95, 0.96};
0974 
0975     const size_t bini = binIndex(j.pt(), binedges_pt);
0976     return binvals[bini];
0977   }
0978 
0979   /// @brief Return the efficiency of the ATLAS JVT tagger at > 0.7 W.P.
0980   ///
0981   /// Values taken from fig 17, https://arxiv.org/pdf/1510.03823.pdf.
0982   /// Shockingly, its run1, but it's still the most recent ATLAS JVT public plots
0983   /// And is still cited by all the Run 2 papers
0984   /// Note! The exact translation of weak/medium/tight varies across runs/years
0985   /// If possible double check the score cut off used.
0986   inline double ATLAS_JVT_EFF_TIGHT(const Jet & j){
0987     if (j.abseta() > 2.4) return 1.0;
0988     // No data for jets pt < 20, hopefully not relevant:
0989     if (j.pt() <= 20*GeV || j.pt() >= 60*GeV) return 1.0;
0990 
0991     const static vector<double> binedges_pt = {20*GeV,25*GeV,30*GeV,35*GeV,40*GeV,45*GeV,50*GeV,55*GeV,60*GeV};
0992     const static vector<double> binvals = {0.81, 0.84, 0.87, 0.90, 0.91, 0.92, 0.93, 0.94};
0993 
0994     const size_t bini = binIndex(j.pt(), binedges_pt);
0995     return binvals[bini];
0996   }
0997 
0998   /// @}
0999 
1000   /// @}
1001 
1002 
1003 }
1004 
1005 #endif