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