rivet is hosted by Hepforge, IPPP Durham
ATLAS_2012_I1119557.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #include "Rivet/Analysis.hh"
00003 #include "Rivet/Projections/FastJets.hh"
00004 #include "Rivet/Projections/FinalState.hh"
00005 
00006 namespace Rivet {
00007 
00008 
00009   namespace { // unnamed namespace
00010     // Forward declarations of calculator functions: implementations at bottom of file
00011     double getWidth(const Jet& jet);
00012     /// @todo Re-enable eccentricity calculation
00013     // double getEcc(const Jet& jet);
00014     double getPFlow(const Jet& jet);
00015     double getAngularity(const Jet& jet);
00016   }
00017 
00018 
00019   class ATLAS_2012_I1119557 : public Analysis {
00020   public:
00021 
00022     ATLAS_2012_I1119557()
00023       : Analysis("ATLAS_2012_I1119557")
00024     {    }
00025 
00026 
00027     void init() {
00028       const FinalState fs;
00029       addProjection(fs, "FinalState");
00030 
00031       FastJets fj06(fs, FastJets::ANTIKT, 0.6);
00032       addProjection(fj06, "AntiKT06");
00033       FastJets fj10(fs, FastJets::ANTIKT, 1.0);
00034       addProjection(fj10, "AntiKT10");
00035 
00036       for (size_t alg = 0; alg < 2; ++alg) {
00037         _hs_mass[alg]  = bookHisto1D(1, alg+1, 1);
00038         _hs_width[alg] = bookHisto1D(2, alg+1, 1);
00039         /// @todo Commented eccentricity out for now: reinstate
00040         // _hs_eccentricity[alg] = bookHisto1D(3, alg+1, 1);
00041       }
00042       _h_planarFlow = bookHisto1D(4, 2, 1);
00043       _h_angularity = bookHisto1D(5, 1, 1);
00044 
00045     }
00046 
00047 
00048     /// Perform the per-event analysis
00049     void analyze(const Event& event) {
00050       const double weight = event.weight();
00051       Jets jetAr[2];
00052       jetAr[0] = applyProjection<FastJets>(event, "AntiKT06").jetsByPt(Cuts::pT > 300*GeV && Cuts::abseta < 2.0);
00053       jetAr[1] = applyProjection<FastJets>(event, "AntiKT10").jetsByPt(Cuts::pT > 300*GeV && Cuts::abseta < 2.0);
00054 
00055       for (size_t alg = 0; alg < 2; ++alg) {
00056         // Require at least one jet
00057         if (jetAr[alg].size() < 1) continue;
00058 
00059         // The leading jet
00060         const Jet& jet = jetAr[alg][0];
00061         const double m   = jet.mass();
00062         const double eta = jet.eta();
00063 
00064         _hs_mass[alg]->fill(m/GeV, weight);
00065         _hs_width[alg]->fill(getWidth(jet), weight);
00066         /// @todo Commented eccentricity out for now: reinstate
00067         // if (fabs(eta) < 0.7 && m > 100*GeV) _hs_eccentricity[alg]->fill(getEcc(jet), weight);
00068 
00069         if (fabs(eta) < 0.7) {
00070           if (alg == 0 && inRange(m/GeV, 100., 130.)) _h_angularity->fill(getAngularity(jet), weight);
00071           if (alg == 1 && inRange(m/GeV, 130., 210.)) _h_planarFlow->fill(getPFlow(jet), weight);
00072         }
00073       }
00074     }
00075 
00076 
00077     /// Normalise histograms etc., after the run
00078     void finalize() {
00079       for (size_t alg = 0; alg < 2; ++alg) {
00080         normalize(_hs_mass[alg]);
00081         normalize(_hs_width[alg]);
00082         /// @todo Commented eccentricity out for now: reinstate
00083         // normalize(_hs_eccentricity[alg]);
00084       }
00085       normalize(_h_planarFlow);
00086       normalize(_h_angularity);
00087     }
00088 
00089 
00090   private:
00091 
00092     Histo1DPtr _hs_mass[2];
00093     Histo1DPtr _hs_width[2];
00094     /// @todo Commented eccentricity out for now: reinstate
00095     // Histo1DPtr _hs_eccentricity[2];
00096     Histo1DPtr _h_planarFlow;
00097     Histo1DPtr _h_angularity;
00098   };
00099 
00100 
00101   DECLARE_RIVET_PLUGIN(ATLAS_2012_I1119557);
00102 
00103 
00104 
00105   namespace { // unnamed namespace
00106 
00107     // Adapted code from Lily
00108     /// @todo Convert to use the Rivet rotation matrix code (should be simpler)
00109     FourMomentum RotateAxes(const Rivet::FourMomentum& p, double M[3][3]){
00110       double px_rot = M[0][0]*(p.px()) + M[0][1]*(p.py()) + M[0][2]*(p.pz());
00111       double py_rot = M[1][0]*(p.px()) + M[1][1]*(p.py()) + M[1][2]*(p.pz());
00112       double pz_rot = M[2][0]*(p.px()) + M[2][1]*(p.py()) + M[2][2]*(p.pz());
00113       return FourMomentum(p.E(), px_rot, py_rot, pz_rot);
00114     }
00115 
00116     // Untouched code from Lily
00117     /// @todo Convert to use the Rivet rotation matrix code (should be simpler)
00118     void CalcRotationMatrix(double nvec[3],double rot_mat[3][3]){
00119       // clear momentum tensor
00120       for (size_t i = 0; i < 3; i++) {
00121         for (size_t j = 0; j < 3; j++) {
00122           rot_mat[i][j] = 0.;
00123         }
00124       }
00125       double mag3 = sqrt(nvec[0]*nvec[0] + nvec[1]*nvec[1]+ nvec[2]*nvec[2]);
00126       double mag2 = sqrt(nvec[0]*nvec[0] + nvec[1]*nvec[1]);
00127       /// @todo cout is not a valid response to a numerical error! Is the error condition reported?!? Assert added by AB for Rivet 1.8.2
00128       assert(mag3 > 0);
00129       if (mag3 <= 0) {
00130         cout << "rotation axis is null" << endl;
00131         return;
00132       }
00133 
00134       double ctheta0 = nvec[2]/mag3;
00135       double stheta0 = mag2/mag3;
00136       double cphi0 = (mag2 > 0.) ? nvec[0]/mag2 : 0;
00137       double sphi0 = (mag2 > 0.) ? nvec[1]/mag2 : 0;
00138 
00139       rot_mat[0][0] = -ctheta0*cphi0;
00140       rot_mat[0][1] = -ctheta0*sphi0;
00141       rot_mat[0][2] = stheta0;
00142       rot_mat[1][0] = sphi0;
00143       rot_mat[1][1] = -1.*cphi0;
00144       rot_mat[1][2] = 0.;
00145       rot_mat[2][0] = stheta0*cphi0;
00146       rot_mat[2][1] = stheta0*sphi0;
00147       rot_mat[2][2] = ctheta0;
00148     }
00149 
00150 
00151     /// Jet width calculation
00152     double getWidth(const Jet& jet) {
00153       const double phi_jet = jet.phi();
00154       const double eta_jet = jet.eta();
00155       double width(0), pTsum(0);
00156       foreach (const Particle& p, jet.particles()) {
00157         double pT = p.pT();
00158         double eta = p.eta();
00159         double phi = p.phi();
00160         width += sqrt(pow(phi_jet - phi,2) + pow(eta_jet - eta, 2)) * pT;
00161         pTsum += pT;
00162       }
00163       const double rtn = (pTsum != 0.0) ? width/pTsum : -1;
00164       return rtn;
00165     }
00166 
00167 
00168     /// Eccentricity calculation, copied and adapted from Lily's code
00169     /// @todo Re-enable eccentricity calculation
00170     // double getEcc(const Jet& jet) {
00171     //   vector<double> phis;
00172     //   vector<double> etas;
00173     //   vector<double> energies;
00174 
00175     //   double etaSum(0), phiSum(0), eTot(0);
00176     //   foreach (const Particle& p, jet.particles()) {
00177     //     const double E = p.E();
00178     //     const double eta = p.eta();
00179 
00180     //     energies.push_back(E);
00181     //     etas.push_back(jet.eta() - eta);
00182 
00183     //     eTot   += E;
00184     //     etaSum += eta * E;
00185 
00186     //     /// @todo Replace with the Rivet deltaPhi function (or the mapAngleTo* functions)
00187     //     double dPhi = jet.phi() - p.phi();
00188     //     //if DPhi does not lie within 0 < DPhi < PI take 2*PI off DPhi
00189     //     //this covers cases where DPhi is greater than PI
00190     //     if( fabs( dPhi - TWOPI ) < fabs(dPhi) ) dPhi -= TWOPI;
00191     //     //if DPhi does not lie within -PI < DPhi < 0 add 2*PI to DPhi
00192     //     //this covers cases where DPhi is less than -PI
00193     //     else if( fabs(dPhi + TWOPI) < fabs(dPhi) ) dPhi += TWOPI;
00194     //     phis.push_back(dPhi);
00195 
00196     //     phiSum += dPhi * E;
00197     //   }
00198 
00199     //   //these are the "pull" away from the jet axis
00200     //   etaSum = etaSum/eTot;
00201     //   phiSum = phiSum/eTot;
00202 
00203     //   // now for every cluster we alter its position by moving it:
00204     //   // away from the new axis if it is in the direction of -ve pull
00205     //   // closer to the new axis if it is in the direction of +ve pull
00206     //   // the effect of this will be that the new energy weighted center will be on the old jet axis.
00207     //   double little_x(0), little_y(0);
00208     //   for (size_t k = 0; k < jet.particles().size(); ++k) {
00209     //     little_x+= etas[k]-etaSum;
00210     //     little_y+= phis[k]-phiSum;
00211     //     etas[k] = etas[k]-etaSum;
00212     //     phis[k] = phis[k]-phiSum;
00213     //   }
00214 
00215     //   double x1(0), x2(0);
00216     //   for (size_t i = 0; i < jet.particles().size(); ++i) {
00217     //     x1 += 2. * energies[i]* etas[i] * phis[i]; // this is 2*X*Y
00218     //     x2 += energies[i]*(phis[i] * phis[i] - etas[i] * etas[i] ); // this is X^2 - Y^2
00219     //   }
00220 
00221     //   // Variance calculations
00222     //   double theta = .5*atan2(x1, x2);
00223     //   double sinTheta =sin(theta);
00224     //   double cosTheta = cos(theta);
00225     //   double theta2 = theta + 0.5*PI;
00226     //   double sinThetaPrime = sin(theta2);
00227     //   double cosThetaPrime = cos(theta2);
00228 
00229     //   double varX(0), varY(0);
00230     //   for (size_t i = 0; i < jet.particles().size(); i++) {
00231     //     const double x = sinTheta*etas[i] + cosTheta*phis[i];
00232     //     const double y = sinThetaPrime*etas[i] + cosThetaPrime*phis[i];
00233     //     varX += energies[i]* sqr(x);
00234     //     varY += energies[i]* sqr(y);
00235     //   }
00236     //   const double varianceMax = max(varX, varY);
00237     //   const double varianceMin = min(varX, varY);
00238     //   const double ecc = (varianceMax != 0.0) ? 1 - varianceMin/varianceMax : -1;
00239     //   return ecc;
00240     // }
00241 
00242 
00243     /// Planar flow calculation, copied and adapted from Lily's code
00244     double getPFlow(const Jet& jet) {
00245       const double phi0 = jet.phi();
00246       const double eta0 = jet.eta();
00247 
00248       double nref[3]; ///< @todo 3-vector to rotate x to? Use Rivet vector classes
00249       nref[0] = cos(phi0)/cosh(eta0);
00250       nref[1] = sin(phi0)/cosh(eta0);
00251       nref[2] = tanh(eta0);
00252 
00253       // Rotation matrix to align with nref
00254       double rotationMatrix[3][3];
00255       CalcRotationMatrix(nref, rotationMatrix);
00256 
00257       double iw00(0.), iw01(0.), iw11(0.), iw10(0.);
00258       foreach (const Particle& p, jet.particles()) {
00259         double a = 1./(p.E()*jet.mass());
00260         FourMomentum rotclus = RotateAxes(p.momentum(), rotationMatrix);
00261         iw00 += a*pow(rotclus.px(), 2);
00262         iw01 += a*rotclus.px()*rotclus.py();
00263         iw10 += a*rotclus.py()*rotclus.px();
00264         iw11 += a*pow(rotclus.py(), 2);
00265       }
00266 
00267       const double det = iw00*iw11 - iw01*iw10;
00268       const double trace = iw00 + iw11;
00269 
00270       const double pf = (trace != 0.0) ? (4.0*det)/sqr(trace) : -1;
00271       return pf;
00272     }
00273 
00274 
00275     /// Angularity calculation, copied and adapted from Lily's code
00276     double getAngularity(const Jet& jet) {
00277       double sum_a = 0.;
00278       // a can take any value < 2 (e.g. 1,0,-0.5 etc) for infrared safety
00279       const double a = -2.;
00280       foreach (const Particle& p, jet.particles()) {
00281         double e_i       = p.E();
00282         double theta_i   = jet.momentum().angle(p.momentum());
00283         double e_theta_i = e_i * pow(sin(theta_i), a) * pow(1-cos(theta_i), 1-a);
00284         sum_a += e_theta_i;
00285       }
00286       const double rtn = (jet.mass() != 0.0 && !std::isnan(sum_a)) ? sum_a/jet.mass() : -1;
00287       return rtn;
00288     }
00289 
00290   }
00291 
00292 
00293 }