Rivet analyses

Analysis of kinematics in 𝒪 → 𝒪ππ decays

Experiment: ()

Status: VALIDATED

Authors: - Peter Richardson

References: none listed

Beams: * *

Beam energies: ANY

Run details: - Any type of process producing bottom or charmonium mesons

Analysis of the kinematics in the decay of bottom and charmonium resonances to lighter resonances and ππ

Source code:MC_DECAY_ONIUM_PIPI.cc

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

namespace Rivet {


  class MC_DECAY_ONIUM_PIPI : public Analysis {
  public:

    /// Constructor
    RIVET_DEFAULT_ANALYSIS_CTOR(MC_DECAY_ONIUM_PIPI);


    /// @name Analysis methods
    /// @{

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

      // Initialise and register projections
      declare(UnstableParticles(), "UFS");
      // psi 2S
      bookHistos(100443, 443, 0.6);
      // psi(3770)
      bookHistos(30443, 443, 0.7);
      // Upsilon (4S)
      bookHistos(300553, 553, 1.2);
      bookHistos(300553, 100553, 0.6);
      // Upsilon (3S)
      bookHistos(200553, 553, 0.9);
      bookHistos(200553, 100553, 0.4);
      // Upsilon (2S)
      bookHistos(100553, 553, 0.6);
    }

    void bookHistos(int id1, int id2, double deltaM) {
      double twompi = 0.378;
      _incoming.push_back(id1);
      _outgoing.push_back(id2);
      std::ostringstream title;
      title << "h_" << id1 << "_" << id2 << "_";
      _mpipi.push_back(make_pair(Histo1DPtr(), Histo1DPtr()));
      book(_mpipi.back().first, title.str() + "mpippim", 100, twompi / GeV, deltaM / GeV);
      book(_mpipi.back().second, title.str() + "mpi0pi0", 100, twompi / GeV, deltaM / GeV);
      _hel.push_back(make_pair(Histo1DPtr(), Histo1DPtr()));
      book(_hel.back().first, title.str() + "hpippim", 100, -1., 1.);
      book(_hel.back().second, title.str() + "hpi0pi0", 100, 0., 1.);
    }

    void findDecayProducts(const Particle& mother,
                           unsigned int& nstable,
                           Particles& pip,
                           Particles& pim,
                           Particles& pi0,
                           Particles& onium) {
      for (const Particle& p : mother.children()) {
        int id = p.pid();
        if (id == PID::PIMINUS) {
          pim.push_back(p);
          ++nstable;
        }
        else if (id == PID::PIPLUS) {
          pip.push_back(p);
          ++nstable;
        }
        else if (id == PID::PI0) {
          pi0.push_back(p);
          ++nstable;
        }
        else if (abs(id) % 1000 == 443 || abs(id) % 1000 == 553) {
          onium.push_back(p);
          ++nstable;
        }
        else if (!p.children().empty()) {
          findDecayProducts(p, nstable, pip, pim, pi0, onium);
        }
        else
          ++nstable;
      }
    }

    /// Perform the per-event analysis
    void analyze(const Event& event) {
      // loop over unstable particles
      for (const Particle& vMeson : apply<UnstableParticles>(event, "UFS").particles()) {
        int id = vMeson.pid();
        if (id % 1000 != 443 && id % 1000 != 553) continue;
        unsigned int nstable(0);
        Particles pip, pim, pi0, onium;
        findDecayProducts(vMeson, nstable, pip, pim, pi0, onium);
        // check for onium
        if (onium.size() != 1 || nstable != 3) continue;
        // check for pipi
        if (!((pip.size() == 1 && pim.size() == 1) || pi0.size() == 2)) continue;
        // check if histos already made
        unsigned int iloc = 0;
        bool found(false);
        while (!found && iloc < _incoming.size()) {
          if (_incoming[iloc] == vMeson.pid() && _outgoing[iloc] == onium[0].pid())
            found = true;
          else
            ++iloc;
        }
        // if histos not made, make them
        if (!found) {
          MSG_WARNING("MC_DECAY_ONIUM_PIPI" << vMeson.pid() << " " << onium[0].pid() << " "
                                            << vMeson.mass() - onium[0].mass() << "\n");
          continue;
        }
        // boost to rest frame of the pion pair
        FourMomentum q = vMeson.momentum() - onium[0].momentum();
        LorentzTransform boost = LorentzTransform::mkFrameTransformFromBeta(q.betaVec());
        FourMomentum qp = onium[0].momentum();
        FourMomentum ppi = pip.size() == 1 ? pip[0].momentum() : pi0[0].momentum();
        qp = boost.transform(qp);
        ppi = boost.transform(ppi);
        double cX = -ppi.p3().unit().dot(qp.p3().unit());
        if (pi0.size() == 2) {
          _mpipi[iloc].second->fill(q.mass());
          _hel[iloc].second->fill(abs(cX));
        }
        else {
          _mpipi[iloc].first->fill(q.mass());
          _hel[iloc].first->fill(cX);
        }
      }
    }


    /// Normalise histograms etc., after the run
    void finalize() {

      // normalize to unity
      for (unsigned int ix = 0; ix < _mpipi.size(); ++ix) {
        normalize(_mpipi[ix].first);
        normalize(_mpipi[ix].second);
        normalize(_hel[ix].first);
        normalize(_hel[ix].second);
      }
    }

    /// @}

    /**
     *  Incoming onium states
     */
    vector<long> _incoming;

    /**
     *  Outgoing onium states
     */
    vector<long> _outgoing;

    /**
     *  Histograms for the \f$\pi^+\pi^-\f$ masses
     */
    vector<pair<Histo1DPtr, Histo1DPtr>> _mpipi;

    /**
     *  Histmgrams for the helicity angles
     */
    vector<pair<Histo1DPtr, Histo1DPtr>> _hel;
  };


  RIVET_DECLARE_PLUGIN(MC_DECAY_ONIUM_PIPI);

}