Rivet analyses

$K_S^0$ and $\Lambda$ production at 0.9 and 7 TeV with ATLAS

Experiment: ATLAS (LHC)

Inspire ID: 944826

Status: VALIDATED

Authors: - Holger Schulz

References: - Expt page: ATLAS-STDM-2010-09 - arXiv: 1111.1297v2 - Phys.Rev. D85 (2012) 012001

Beams: p+ p+

Beam energies: (450.0, 450.0); (3500.0, 3500.0)GeV

Run details: - QCD events, 900~GeV and 7~TeV. $\Lambda$ and $K_S$ must be able to decay. Allow only charged decay modes to improve efficiency.

The production of $K_S$ and $\Lambda$ hadrons is studied in inelastic $pp$ collisions at $\sqrt{s} = 0.9$ and 7 TeV collected with the ATLAS detector at the LHC using a minimum-bias trigger. The observed distributions of transverse momentum, rapidity, and multiplicity are corrected to hadron level in a model-independent way within well defined phase-space regions. The distribution of the production ratio of $\bar{\Lambda}$ to $\Lambda$ baryons is also measured. The results are compared with various Monte Carlo simulation models. Although most of these models agree with data to within 15 percent in the $K_S$ distributions, substantial disagreements with data are found in the $\Lambda$ distributions of transverse momentum.

Source code:ATLAS_2011_I944826.cc

// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/ChargedFinalState.hh"
#include "Rivet/Projections/IdentifiedFinalState.hh"
#include "Rivet/Projections/UnstableParticles.hh"

namespace Rivet {


  class ATLAS_2011_I944826 : public Analysis {
  public:

    /// Constructor
    RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2011_I944826);

    /// Book histograms and initialise projections before the run
    void init() {

      UnstableParticles ufs(Cuts::pT > 100 * MeV);
      declare(ufs, "UFS");

      ChargedFinalState mbts(Cuts::absetaIn(2.09, 3.84));
      declare(mbts, "MBTS");

      IdentifiedFinalState nstable(Cuts::abseta < 2.5 && Cuts::pT >= 100 * MeV);
      nstable.acceptIdPair(PID::ELECTRON)
          .acceptIdPair(PID::MUON)
          .acceptIdPair(PID::PIPLUS)
          .acceptIdPair(PID::KPLUS)
          .acceptIdPair(PID::PROTON);
      declare(nstable, "nstable");


      for (double eVal : allowedEnergies()) {

        const string en = toString(round(eVal));
        if (isCompatibleWithSqrtS(eVal)) _sqs = en;

        bool is900(en == "900");
        size_t offset = is900 ? 3 : 0;
        book(_h[en + "Ks_pT"], 1 + offset, 1, 1);
        book(_h[en + "Ks_y"], 2 + offset, 1, 1);
        book(_h[en + "Ks_mult"], 3 + offset, 1, 1);
        book(_h[en + "L_pT"], 7 + offset, 1, 1);
        book(_h[en + "L_y"], 8 + offset, 1, 1);
        book(_h[en + "L_mult"], 9 + offset, 1, 1);
        if (is900) offset = 2;
        book(_e[en + "v_y"], 13 + offset, 1, 1);
        book(_e[en + "v_pT"], 14 + offset, 1, 1);
        //
        book(_h[en + "lambda_v_y"], "TMP/lambda_v_y_" + en, is900 ? 5 : 10, 0.0, 2.5);
        book(_h[en + "lambdabar_v_y"], "TMP/lambdabar_v_y_" + en, is900 ? 5 : 10, 0.0, 2.5);
        book(_h[en + "lambda_v_pT"], "TMP/lambda_v_pT_" + en, is900 ? 8 : 18, 0.5, is900 ? 3.7 : 4.1);
        book(_h[en + "lambdabar_v_pT"], "TMP/lambdabar_v_pT_" + en, is900 ? 8 : 18, 0.5, is900 ? 3.7 : 4.1);
      }
      raiseBeamErrorIf(_sqs.empty());
    }


    // This function is required to impose the flight time cuts on Kaons and Lambdas
    double getPerpFlightDistance(const Rivet::Particle& p) {
      ConstGenParticlePtr genp = p.genParticle();
      ConstGenVertexPtr prodV = genp->production_vertex();
      ConstGenVertexPtr decV = genp->end_vertex();
      RivetHepMC::FourVector prodPos = prodV->position();
      if (decV) {
        const RivetHepMC::FourVector decPos = decV->position();
        double dy = prodPos.y() - decPos.y();
        double dx = prodPos.x() - decPos.x();
        return add_quad(dx, dy);
      }
      return numeric_limits<double>::max();
    }


    bool daughtersSurviveCuts(const Rivet::Particle& p) {
      // We require the Kshort or Lambda to decay into two charged
      // particles with at least pT = 100 MeV inside acceptance region
      ConstGenParticlePtr genp = p.genParticle();
      ConstGenVertexPtr decV = genp->end_vertex();
      bool decision = true;

      if (!decV) return false;
      if (HepMCUtils::particles(decV, Relatives::CHILDREN).size() == 2) {
        std::vector<double> pTs;
        std::vector<int> charges;
        std::vector<double> etas;
        for (ConstGenParticlePtr gp : HepMCUtils::particles(decV, Relatives::CHILDREN)) {
          pTs.push_back(gp->momentum().perp());
          etas.push_back(fabs(gp->momentum().eta()));
          charges.push_back(Rivet::PID::charge3(gp->pdg_id()));
          // gp->print();
        }
        if ((pTs[0] / Rivet::GeV < 0.1) || (pTs[1] / Rivet::GeV < 0.1)) {
          decision = false;
          MSG_DEBUG("Failed pT cut: " << pTs[0] / Rivet::GeV << " " << pTs[1] / Rivet::GeV);
        }
        if (etas[0] > 2.5 || etas[1] > 2.5) {
          decision = false;
          MSG_DEBUG("Failed eta cut: " << etas[0] << " " << etas[1]);
        }
        if (charges[0] * charges[1] >= 0) {
          decision = false;
          MSG_DEBUG("Failed opposite charge cut: " << charges[0] << " " << charges[1]);
        }
      }
      else {
        decision = false;
        MSG_DEBUG("Failed nDaughters cut: " << HepMCUtils::particles(decV, Relatives::CHILDREN).size());
      }

      return decision;
    }

    /// Perform the per-event analysis
    void analyze(const Event& event) {

      // ATLAS MBTS trigger requirement of at least one hit in either hemisphere
      if (apply<FinalState>(event, "MBTS").size() < 1) {
        MSG_DEBUG("Failed trigger cut");
        vetoEvent;
      }

      // Veto event also when we find less than 2 particles in the acceptance region of type 211,2212,11,13,321
      if (apply<FinalState>(event, "nstable").size() < 2) {
        MSG_DEBUG("Failed stable particle cut");
        vetoEvent;
      }

      // This ufs holds all the Kaons and Lambdas
      const UnstableParticles& ufs = apply<UnstableParticles>(event, "UFS");

      // Some conters
      int n_KS0 = 0, n_LAMBDA = 0;

      // Particle loop
      for (const Particle& p : ufs.particles()) {

        // General particle quantities
        const double pT = p.pT();
        const double y = p.rapidity();
        const PdgId apid = p.abspid();

        double flightd = 0.0;

        // Look for Kaons, Lambdas
        switch (apid) {

          case PID::K0S:
            flightd = getPerpFlightDistance(p);
            if (!inRange(flightd / mm, 4., 450.)) {
              MSG_DEBUG("Kaon failed flight distance cut:" << flightd);
              break;
            }
            if (daughtersSurviveCuts(p)) {
              _h[_sqs + "Ks_y"]->fill(y);
              _h[_sqs + "Ks_pT"]->fill(pT / GeV);
              ++n_KS0;
            }
            break;

          case PID::LAMBDA:
            if (pT < 0.5 * GeV) { // Lambdas have an additional pT cut of 500 MeV
              MSG_DEBUG("Lambda failed pT cut:" << pT / GeV << " GeV");
              break;
            }
            flightd = getPerpFlightDistance(p);
            if (!inRange(flightd / mm, 17., 450.)) {
              MSG_DEBUG("Lambda failed flight distance cut:" << flightd / mm << " mm");
              break;
            }
            if (daughtersSurviveCuts(p)) {
              if (p.pid() == PID::LAMBDA) {
                _h[_sqs + "lambda_v_y"]->fill(fabs(y));
                _h[_sqs + "lambda_v_pT"]->fill(pT / GeV);
                _h[_sqs + "L_y"]->fill(y);
                _h[_sqs + "L_pT"]->fill(pT / GeV);
                ++n_LAMBDA;
              }
              else if (p.pid() == -PID::LAMBDA) {
                _h[_sqs + "lambdabar_v_y"]->fill(fabs(y));
                _h[_sqs + "lambdabar_v_pT"]->fill(pT / GeV);
              }
            }
            break;
        }
      }

      // Fill multiplicity histos
      _h[_sqs + "Ks_mult"]->fill(n_KS0);
      _h[_sqs + "L_mult"]->fill(n_LAMBDA);
    }


    /// Normalise histograms etc., after the run
    void finalize() {
      // Division of histograms to obtain lambda_bar/lambda ratios
      for (double eVal : allowedEnergies()) {
        const string en = toString(round(eVal));

        if (_h[en + "L_pT"]->sumW()) scale(_h[en + "L_y"], 1.0 / _h[en + "L_pT"]->sumW());
        divide(_h[en + "lambdabar_v_y"], _h[en + "lambda_v_y"], _e[en + "v_y"]);
        divide(_h[en + "lambdabar_v_pT"], _h[en + "lambda_v_pT"], _e[en + "v_pT"]);
      }
      normalize(_h);
    }


  private:

    /// @name Persistent histograms
    /// @{
    map<string, Histo1DPtr> _h;
    map<string, Estimate1DPtr> _e;

    string _sqs = "";
    /// @}
  };


  RIVET_DECLARE_PLUGIN(ATLAS_2011_I944826);

}