ATLAS_2010_S8894728.cc

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #include "Rivet/Analysis.hh"
00003 #include "Rivet/RivetAIDA.hh"
00004 #include "Rivet/Projections/ChargedFinalState.hh"
00005 #include "Rivet/Tools/Logging.hh"
00006 #include "LWH/Profile1D.h"
00007 #include "LWH/Histogram1D.h"
00008 
00009 namespace Rivet {
00010 
00011 
00012   namespace {
00013 
00014     inline void _moments_to_stddev(AIDA::IProfile1D* moment_profiles[], AIDA::IDataPointSet* target_dps) {
00015       for (int b = 0; b < target_dps->size(); ++b) { // loop over bins
00016         /// @todo Assuming unit weights here! Should use N_effective = sumW**2/sumW2? How?
00017         const double numentries = moment_profiles[0]->binEntries(b);
00018         const double var = moment_profiles[1]->binHeight(b) - intpow(moment_profiles[0]->binHeight(b), 2);
00019         const double sd = isZero(var) ? 0 : sqrt(var); //< Numerical safety check
00020         target_dps->point(b)->coordinate(1)->setValue(sd);
00021         if (sd == 0 || numentries < 3) {
00022           cerr << "Need at least 3 bin entries and a non-zero central value to calculate "
00023                << "an error on standard deviation profiles (bin " << b << ")" << endl;
00024           target_dps->point(b)->coordinate(1)->setErrorPlus(0);
00025           target_dps->point(b)->coordinate(1)->setErrorMinus(0);
00026           continue;
00027         }
00028         // c2(y) = m4(x) - 4 m3(x) m1(x) - m2(x)^2 + 8 m2(x) m1(x)^2 - 4 m1(x)^4
00029         const double var_on_var = moment_profiles[3]->binHeight(b)
00030           - 4 * moment_profiles[2]->binHeight(b) * moment_profiles[0]->binHeight(b)
00031           - intpow(moment_profiles[1]->binHeight(b), 2)
00032           + 8 * moment_profiles[1]->binHeight(b) * intpow(moment_profiles[0]->binHeight(b), 2)
00033           - 4 * intpow(moment_profiles[0]->binHeight(b), 4);
00034         const double stderr_on_var = sqrt(var_on_var/(numentries-2.0));
00035         const double stderr_on_sd = stderr_on_var / (2.0*sd);
00036         target_dps->point(b)->coordinate(1)->setErrorPlus(stderr_on_sd);
00037         target_dps->point(b)->coordinate(1)->setErrorMinus(stderr_on_sd);
00038       }
00039     }
00040 
00041   }
00042 
00043 
00044   class ATLAS_2010_S8894728 : public Analysis {
00045   public:
00046 
00047     ATLAS_2010_S8894728() : Analysis("ATLAS_2010_S8894728") {    }
00048 
00049 
00050     void init() {
00051       const ChargedFinalState cfs100(-2.5, 2.5, 100*MeV);
00052       addProjection(cfs100, "CFS100");
00053       const ChargedFinalState cfs500(-2.5, 2.5, 500*MeV);
00054       addProjection(cfs500, "CFS500");
00055       const ChargedFinalState cfslead(-2.5, 2.5, 1.0*GeV);
00056       addProjection(cfslead, "CFSlead");
00057 
00058       // Get an index for the beam energy
00059       int isqrts = -1;
00060       if (fuzzyEquals(sqrtS(), 900*GeV)) isqrts = 0;
00061       else if (fuzzyEquals(sqrtS(), 7*TeV)) isqrts = 1;
00062       assert(isqrts >= 0);
00063 
00064       // Nch profiles, 500 MeV track pT cut
00065       _hist_nch_transverse_500[0] = bookProfile1D(1+isqrts, 1, 1);
00066       _hist_nch_toward_500        = bookProfile1D(1+isqrts, 1, 2);
00067       _hist_nch_away_500          = bookProfile1D(1+isqrts, 1, 3);
00068 
00069       // pTsum profiles, 500 MeV track pT cut
00070       _hist_ptsum_transverse_500[0] = bookProfile1D(3+isqrts, 1, 1);
00071       _hist_ptsum_toward_500        = bookProfile1D(3+isqrts, 1, 2);
00072       _hist_ptsum_away_500          = bookProfile1D(3+isqrts, 1, 3);
00073 
00074       // Standard deviation profiles
00075       // First the higher moments of main profiles to calculate variance and error on variance...
00076       for (size_t i = 1; i < 4; ++i) {
00077         _hist_nch_transverse_500[i] = new LWH::Profile1D(binEdges(1+isqrts, 1, 1));
00078         _hist_ptsum_transverse_500[i] = new LWH::Profile1D(binEdges(3+isqrts, 1, 1));
00079       }
00080       // Then the data point sets into which the results will be inserted
00081       _dps_sdnch_transverse_500   = bookDataPointSet(5+isqrts, 1, 1);
00082       _dps_sdptsum_transverse_500 = bookDataPointSet(7+isqrts, 1, 1);
00083 
00084       // <pT> profiles, 500 MeV track pT cut
00085       _hist_ptavg_transverse_500 = bookProfile1D(9+isqrts, 1, 1);
00086       _hist_ptavg_toward_500     = bookProfile1D(9+isqrts, 1, 2);
00087       _hist_ptavg_away_500       = bookProfile1D(9+isqrts, 1, 3);
00088 
00089       // <pT> vs. Nch profiles, 500 MeV track pT cut
00090       _hist_dn_dpt_transverse_500 = bookProfile1D(11+isqrts, 1, 1);
00091       _hist_dn_dpt_toward_500     = bookProfile1D(11+isqrts, 1, 2);
00092       _hist_dn_dpt_away_500       = bookProfile1D(11+isqrts, 1, 3);
00093 
00094       // Nch vs. Delta(phi) profiles, 500 MeV track pT cut
00095       _hist_N_vs_dPhi_1_500 = bookProfile1D(13+isqrts, 1, 1);
00096       _hist_N_vs_dPhi_2_500 = bookProfile1D(13+isqrts, 1, 2);
00097       _hist_N_vs_dPhi_3_500 = bookProfile1D(13+isqrts, 1, 3);
00098       _hist_N_vs_dPhi_5_500 = bookProfile1D(13+isqrts, 1, 4);
00099       // pT vs. Delta(phi) profiles, 500 MeV track pT cut
00100       _hist_pT_vs_dPhi_1_500 = bookProfile1D(15+isqrts, 1, 1);
00101       _hist_pT_vs_dPhi_2_500 = bookProfile1D(15+isqrts, 1, 2);
00102       _hist_pT_vs_dPhi_3_500 = bookProfile1D(15+isqrts, 1, 3);
00103       _hist_pT_vs_dPhi_5_500 = bookProfile1D(15+isqrts, 1, 4);
00104 
00105       // Nch and pTsum profiles, 100 MeV track pT cut
00106       _hist_nch_transverse_100   = bookProfile1D(17+isqrts, 1, 1);
00107       _hist_nch_toward_100       = bookProfile1D(17+isqrts, 1, 2);
00108       _hist_nch_away_100         = bookProfile1D(17+isqrts, 1, 3);
00109       _hist_ptsum_transverse_100 = bookProfile1D(19+isqrts, 1, 1);
00110       _hist_ptsum_toward_100     = bookProfile1D(19+isqrts, 1, 2);
00111       _hist_ptsum_away_100       = bookProfile1D(19+isqrts, 1, 3);
00112 
00113       // Profiles vs. eta (7 TeV only)
00114       if (isqrts == 1) {
00115         _hist_nch_vs_eta_transverse_100   = bookProfile1D(21, 1, 1);
00116         _hist_ptsum_vs_eta_transverse_100 = bookProfile1D(22, 1, 1);
00117       }
00118 
00119     }
00120 
00121 
00122     // Little helper function to identify Delta(phi) regions
00123     inline int region_index(double dphi) {
00124       assert(inRange(dphi, 0.0, PI, CLOSED, CLOSED));
00125       if (dphi < PI/3.0) return 0;
00126       if (dphi < 2*PI/3.0) return 1;
00127       return 2;
00128     }
00129 
00130 
00131     void analyze(const Event& event) {
00132       const double weight = event.weight();
00133 
00134       // Require at least one track in the event with pT >= 1 GeV
00135       const ChargedFinalState& cfslead = applyProjection<ChargedFinalState>(event, "CFSlead");
00136       if (cfslead.size() < 1) {
00137         vetoEvent;
00138       }
00139 
00140       // These are the charged particles (tracks) with pT > 500 MeV
00141       const ChargedFinalState& charged500 = applyProjection<ChargedFinalState>(event, "CFS500");
00142 
00143       // Identify leading track and its phi and pT (this is the same for both the 100 MeV and 500 MeV track cuts)
00144       ParticleVector particles500 = charged500.particlesByPt();
00145       Particle p_lead = particles500[0];
00146       const double philead = p_lead.momentum().phi();
00147       const double etalead = p_lead.momentum().eta();
00148       const double pTlead  = p_lead.momentum().perp();
00149       MSG_DEBUG("Leading track: pT = " << pTlead << ", eta = " << etalead << ", phi = " << philead);
00150 
00151       // Iterate over all > 500 MeV particles and count particles and scalar pTsum in the three regions
00152       vector<double> num500(3, 0), ptSum500(3, 0.0);
00153       // Temporary histos that bin Nch and pT in dPhi.
00154       // NB. Only one of each needed since binnings are the same for the energies and pT cuts
00155       LWH::Histogram1D hist_num_dphi_500(binEdges(13,1,1));
00156       LWH::Histogram1D hist_pt_dphi_500(binEdges(15,1,1));
00157       foreach (const Particle& p, particles500) {
00158         const double pT = p.momentum().pT();
00159         const double dPhi = deltaPhi(philead, p.momentum().phi());
00160         const int ir = region_index(dPhi);
00161         num500[ir] += 1;
00162         ptSum500[ir] += pT;
00163 
00164         // Fill temp histos to bin Nch and pT in dPhi
00165         if (p.genParticle() != p_lead.genParticle()) { // We don't want to fill all those zeros from the leading track...
00166           hist_num_dphi_500.fill(dPhi, 1);
00167           hist_pt_dphi_500.fill(dPhi, pT);
00168         }
00169       }
00170 
00171 
00172       // Iterate over charged particles again for profiles against Nch
00173       // This is necessary since the Nch are region-specific and so are only known after the first loop
00174       foreach (const Particle& p, particles500) {
00175         const double pT = p.momentum().pT();
00176         const double dPhi = deltaPhi(philead, p.momentum().phi());
00177         const int ir = region_index(dPhi);
00178         switch (ir) {
00179         case 0:
00180           _hist_dn_dpt_toward_500->fill(num500[0], pT, weight);
00181           break;
00182         case 1:
00183           _hist_dn_dpt_transverse_500->fill(num500[1], pT, weight);
00184           break;
00185         case 2:
00186           _hist_dn_dpt_away_500->fill(num500[2], pT, weight);
00187           break;
00188         default:
00189           assert(false && "How did we get here?");
00190         }
00191       }
00192 
00193 
00194       // Now fill underlying event histograms
00195       // The densities are calculated by dividing the UE properties by dEta*dPhi
00196       // -- each region has a dPhi of 2*PI/3 and dEta is two times 2.5
00197       const double dEtadPhi = (2*2.5 * 2*PI/3.0);
00198       // Transverse profiles need 4 orders of moments for stddev with errors
00199       for (int i = 0; i < 4; ++i) {
00200         _hist_nch_transverse_500[i]->fill(pTlead/GeV, intpow(num500[1]/dEtadPhi, i+1), weight);
00201         _hist_ptsum_transverse_500[i]->fill(pTlead/GeV, intpow(ptSum500[1]/GeV/dEtadPhi, i+1), weight);
00202       }
00203       // Toward and away profiles only need the first moment (the mean)
00204       _hist_nch_toward_500->fill(pTlead/GeV, num500[0]/dEtadPhi, weight);
00205       _hist_nch_away_500->fill(pTlead/GeV, num500[2]/dEtadPhi, weight);
00206       _hist_ptsum_toward_500->fill(pTlead/GeV, ptSum500[0]/GeV/dEtadPhi, weight);
00207       _hist_ptsum_away_500->fill(pTlead/GeV, ptSum500[2]/GeV/dEtadPhi, weight);
00208       // <pT> profiles
00209       //MSG_INFO("Trans pT1, pTsum, Nch, <pT>" << pTlead/GeV << ", " <<  ptSum500[1]/GeV << ", " << num500[1] << ", " << ptSum500[1]/GeV/num500[1]);
00210       if (num500[1] > 0) _hist_ptavg_transverse_500->fill(pTlead/GeV, ptSum500[1]/GeV/num500[1], weight);
00211       if (num500[0] > 0) _hist_ptavg_toward_500->fill(pTlead/GeV, ptSum500[0]/GeV/num500[0], weight);
00212       if (num500[2] > 0) _hist_ptavg_away_500->fill(pTlead/GeV, ptSum500[2]/GeV/num500[2], weight);
00213 
00214 
00215       // Update the "proper" dphi profile histograms
00216       // Note that we fill dN/dEtadPhi: dEta = 2*2.5, dPhi = 2*PI/nBins
00217       // The values tabulated in the note are for an (undefined) signed Delta(phi) rather than
00218       // |Delta(phi)| and so differ by a factor of 2: we have to actually norm for angular range = 2pi
00219       const size_t nbins = binEdges(13,1,1).size() - 1;
00220       std::vector<double> ptcut;
00221       if (fuzzyEquals(sqrtS(), 900*GeV)) {
00222         ptcut += 1.0; ptcut += 1.5; ptcut += 2.0; ptcut += 2.5;
00223       }
00224       else if (fuzzyEquals(sqrtS(), 7*TeV)) {
00225         ptcut += 1.0; ptcut += 2.0; ptcut += 3.0; ptcut += 5.0;
00226       }
00227       assert(ptcut.size() == 4);
00228       for (size_t i = 0; i < nbins; ++i) {
00229         // First Nch
00230         const double binmean_num = hist_num_dphi_500.binMean(i);
00231         const double binvalue_num = hist_num_dphi_500.binHeight(i)/hist_num_dphi_500.axis().binWidth(i)/10.0;
00232         if (pTlead/GeV >= ptcut[0]) _hist_N_vs_dPhi_1_500->fill(binmean_num, binvalue_num, weight);
00233         if (pTlead/GeV >= ptcut[1]) _hist_N_vs_dPhi_2_500->fill(binmean_num, binvalue_num, weight);
00234         if (pTlead/GeV >= ptcut[2]) _hist_N_vs_dPhi_3_500->fill(binmean_num, binvalue_num, weight);
00235         if (pTlead/GeV >= ptcut[3]) _hist_N_vs_dPhi_5_500->fill(binmean_num, binvalue_num, weight);
00236         // Then pT
00237         const double binmean_pt = hist_pt_dphi_500.binMean(i);
00238         const double binvalue_pt = hist_pt_dphi_500.binHeight(i)/hist_pt_dphi_500.axis().binWidth(i)/10.0;
00239         if (pTlead/GeV >= ptcut[0]) _hist_pT_vs_dPhi_1_500->fill(binmean_pt, binvalue_pt, weight);
00240         if (pTlead/GeV >= ptcut[1]) _hist_pT_vs_dPhi_2_500->fill(binmean_pt, binvalue_pt, weight);
00241         if (pTlead/GeV >= ptcut[2]) _hist_pT_vs_dPhi_3_500->fill(binmean_pt, binvalue_pt, weight);
00242         if (pTlead/GeV >= ptcut[3]) _hist_pT_vs_dPhi_5_500->fill(binmean_pt, binvalue_pt, weight);
00243       }
00244 
00245 
00246       //////////////////////
00247 
00248 
00249       // These are the charged particles (tracks) with pT > 100 MeV
00250       const ChargedFinalState& charged100 = applyProjection<ChargedFinalState>(event, "CFS100");
00251 
00252       // Iterate over all > 100 MeV particles and count particles and scalar pTsum in the three regions
00253       vector<double> num100(3, 0), ptSum100(3, 0.0);
00254       foreach (const Particle& p, charged100.particles()) {
00255         const double pT = p.momentum().pT();
00256         const double dPhi = deltaPhi(philead, p.momentum().phi());
00257         const int ir = region_index(dPhi);
00258         num100[ir] += 1;
00259         ptSum100[ir] += pT;
00260       }
00261 
00262       // Now fill the two sets of 100 MeV underlying event histograms
00263       _hist_nch_transverse_100->fill(pTlead/GeV, num100[1]/dEtadPhi, weight);
00264       _hist_nch_toward_100->fill(pTlead/GeV, num100[0]/dEtadPhi, weight);
00265       _hist_nch_away_100->fill(pTlead/GeV, num100[2]/dEtadPhi, weight);
00266       _hist_ptsum_transverse_100->fill(pTlead/GeV, ptSum100[1]/GeV/dEtadPhi, weight);
00267       _hist_ptsum_toward_100->fill(pTlead/GeV, ptSum100[0]/GeV/dEtadPhi, weight);
00268       _hist_ptsum_away_100->fill(pTlead/GeV, ptSum100[2]/GeV/dEtadPhi, weight);
00269 
00270       // And finally the Nch and pT vs eta_lead profiles (again from > 100 MeV tracks, and only at 7 TeV)
00271       if (fuzzyEquals(sqrtS(), 7*TeV) && pTlead > 5*GeV) {
00272         // MSG_INFO(sqrtS() << " " << pTlead << " " << ptSum100[1]/dEtadPhi << " " << num100[1]/dEtadPhi);
00273         _hist_nch_vs_eta_transverse_100->fill(etalead, num100[1]/dEtadPhi, weight);
00274         _hist_ptsum_vs_eta_transverse_100->fill(etalead, ptSum100[1]/GeV/dEtadPhi, weight);
00275       }
00276 
00277     }
00278 
00279 
00280     void finalize() {
00281       // Convert the various moments of the 500 MeV trans pT and Nch distributions to std devs with correct error
00282       _moments_to_stddev(_hist_nch_transverse_500, _dps_sdnch_transverse_500);
00283       _moments_to_stddev(_hist_ptsum_transverse_500, _dps_sdptsum_transverse_500);
00284     }
00285 
00286 
00287   private:
00288 
00289     AIDA::IProfile1D*  _hist_nch_transverse_500[4];
00290     AIDA::IProfile1D*  _hist_nch_toward_500;
00291     AIDA::IProfile1D*  _hist_nch_away_500;
00292 
00293     AIDA::IProfile1D*  _hist_ptsum_transverse_500[4];
00294     AIDA::IProfile1D*  _hist_ptsum_toward_500;
00295     AIDA::IProfile1D*  _hist_ptsum_away_500;
00296 
00297     AIDA::IDataPointSet*  _dps_sdnch_transverse_500;
00298     AIDA::IDataPointSet*  _dps_sdptsum_transverse_500;
00299 
00300     AIDA::IProfile1D* _hist_ptavg_transverse_500;
00301     AIDA::IProfile1D* _hist_ptavg_toward_500;
00302     AIDA::IProfile1D* _hist_ptavg_away_500;
00303 
00304     AIDA::IProfile1D*  _hist_dn_dpt_transverse_500;
00305     AIDA::IProfile1D*  _hist_dn_dpt_toward_500;
00306     AIDA::IProfile1D*  _hist_dn_dpt_away_500;
00307 
00308     AIDA::IProfile1D*  _hist_N_vs_dPhi_1_500;
00309     AIDA::IProfile1D*  _hist_N_vs_dPhi_2_500;
00310     AIDA::IProfile1D*  _hist_N_vs_dPhi_3_500;
00311     AIDA::IProfile1D*  _hist_N_vs_dPhi_5_500;
00312 
00313     AIDA::IProfile1D*  _hist_pT_vs_dPhi_1_500;
00314     AIDA::IProfile1D*  _hist_pT_vs_dPhi_2_500;
00315     AIDA::IProfile1D*  _hist_pT_vs_dPhi_3_500;
00316     AIDA::IProfile1D*  _hist_pT_vs_dPhi_5_500;
00317 
00318     AIDA::IProfile1D*  _hist_nch_transverse_100;
00319     AIDA::IProfile1D*  _hist_nch_toward_100;
00320     AIDA::IProfile1D*  _hist_nch_away_100;
00321     AIDA::IProfile1D*  _hist_ptsum_transverse_100;
00322     AIDA::IProfile1D*  _hist_ptsum_toward_100;
00323     AIDA::IProfile1D*  _hist_ptsum_away_100;
00324 
00325     AIDA::IProfile1D* _hist_nch_vs_eta_transverse_100;
00326     AIDA::IProfile1D* _hist_ptsum_vs_eta_transverse_100;
00327 
00328   };
00329 
00330 
00331   // This global object acts as a hook for the plugin system
00332   AnalysisBuilder<ATLAS_2010_S8894728> plugin_ATLAS_2010_S8894728;
00333 
00334 }