Rivet analyses


title: PDG_TAUS

Tau branching fractions from PDG

Experiment: PDG (Various)

Inspire ID: 1315584

Status: VALIDATED

Authors: - Holger Schulz

References: - Chin.Phys. C38 (2014) 090001

Beams: * *

Beam energies: ANY

Run details: - Works with collision events and particle guns

Hadronic and leptonic tau decays are analysed to compare to PDG data of branching fractions.

Source code:PDG_TAUS.cc

```c++ // -- C++ --

include "Rivet/Analysis.hh"

include "Rivet/Projections/TauFinder.hh"

include "Rivet/Tools/ParticleUtils.hh"

namespace Rivet {

class PDG_TAUS : public Analysis { public:

/// Constructor
PDG_TAUS()
    : Analysis("PDG_TAUS") { }


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

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

  TauFinder tauleptonic(TauDecay::LEPTONIC); // open cuts, leptonic decays
  declare(tauleptonic, "TauLeptonic");

  TauFinder tauhadronic(TauDecay::HADRONIC); // open cuts, hadronic decays
  declare(tauhadronic, "TauHadronic");

  populateDecayMap();

  book(_h_ratio_mu, 1, 1, 1);
  book(_h_ratio_el, 1, 1, 2);
  book(_h_1prong_pinu, 2, 1, 1);
  book(_h_1prong_Kpnu, 2, 1, 2);
  book(_h_1prong_pipinu, 2, 1, 3);
  book(_h_1prong_Kppinu, 2, 1, 4);
  book(_h_1prong_pipipinu, 2, 1, 5);
  book(_h_1prong_Knpinu, 2, 1, 6);
  book(_h_3prong_pipipinu, 2, 2, 1);
  book(_h_5prong, 2, 3, 1);

  book(_weights_had, "TMP/weights_had");
  book(_weights_mu, "TMP/weights_mu");
  book(_weights_el, "TMP/weights_el");
}


/// Perform the per-event analysis
void analyze(const Event& e) {
  const TauFinder& taulep = apply<TauFinder>(e, "TauLeptonic");
  const TauFinder& tauhad = apply<TauFinder>(e, "TauHadronic");

  // Hadronic tau decays --- prong decays
  for (const Particle& tau : tauhad.taus()) {
    _weights_had->fill();
    int prongs = countProngs(tau); // number of charged particles among decay products
    // Only do 1 prong decays here
    if (prongs == 1) {
      ////// Exclusive decay modes "1-prong"
      if (analyzeDecay(tau, decay_pids["pinu"], true)) _h_1prong_pinu->fill(1);
      if (analyzeDecay(tau, decay_pids["Kpnu"], true)) _h_1prong_Kpnu->fill(1);
      if (analyzeDecay(tau, decay_pids["pipinu"], true)) _h_1prong_pipinu->fill(1);
      if (analyzeDecay(tau, decay_pids["Kppinu"], true)) _h_1prong_Kppinu->fill(1);
      if (analyzeDecay(tau, decay_pids["pipipinu"], true)) _h_1prong_pipipinu->fill(1);
      // Kshort, Klong --- (twice) filling the K0 labelled PDG histo
      if (analyzeDecay(tau, decay_pids["KSpinu"], true)) _h_1prong_Knpinu->fill(1);
      if (analyzeDecay(tau, decay_pids["KLpinu"], true)) _h_1prong_Knpinu->fill(1);
    }
    else if (prongs == 3) {
      if (analyzeDecay(tau, decay_pids["3pipipinu"], true)) _h_3prong_pipipinu->fill(1);
    }
    else if (prongs == 5 && !any(tau.stableDescendants(), HasAbsPID(310)))
      _h_5prong->fill(1);
  }

  // Leptonic tau decays --- look for radiative and non-radiative 1 prong decays
  for (const Particle& tau : taulep.taus()) {
    int prongs = countProngs(tau); // number of charged particles among decay products
    // Only do 1 prong decays here
    if (prongs == 1) {
      analyzeRadiativeDecay(tau, decay_pids["muids"], _weights_mu, true, _h_ratio_mu);
      analyzeRadiativeDecay(tau, decay_pids["elids"], _weights_el, true, _h_ratio_el);
    }
  }
}


/// Normalise histograms etc., after the run
void finalize() {
  scale(_h_ratio_mu, 1. / *_weights_mu);
  scale(_h_ratio_el, 1. / *_weights_el);

  const YODA::Counter norm = *_weights_had + *_weights_mu + *_weights_el;
  scale(_h_1prong_pinu, 1. / norm);
  scale(_h_1prong_Kpnu, 1. / norm);
  scale(_h_1prong_pipinu, 1. / norm);
  scale(_h_1prong_Kppinu, 1. / norm);
  scale(_h_1prong_pipipinu, 1. / norm);
  scale(_h_1prong_Knpinu, 1. / norm);
  scale(_h_3prong_pipipinu, 1. / norm);
  scale(_h_5prong, 1. / norm);
}


// Count charged decay products
int countProngs(Particle mother) {
  int n_prongs = 0;
  for (Particle p : mother.stableDescendants())
    if (p.charge3() != 0) ++n_prongs;
  return n_prongs;
}


// Set up a lookup table for decays
void populateDecayMap() {
  decay_pids["muids"] = {{13, 14, 16}};
  decay_pids["elids"] = {{11, 12, 16}};
  decay_pids["pinu"] = {{211, 16}};
  decay_pids["Kpnu"] = {{321, 16}};
  decay_pids["pipinu"] = {{111, 211, 16}};
  decay_pids["Kppinu"] = {{111, 321, 16}};
  decay_pids["pipipinu"] = {{111, 111, 211, 16}};
  decay_pids["KSpinu"] = {{211, 310, 16}};
  decay_pids["KLpinu"] = {{211, 130, 16}};
  decay_pids["3pipipinu"] = {{211, 211, 211, 16}};
}


bool analyzeDecay(Particle mother, const vector<int>& ids, bool absolute) {
  const Particles parts = {mother};
  return cascadeContains(parts, ids, absolute, true);
}


// Look for radiative (and non-radiative) tau decays to fill a ratio histo
void analyzeRadiativeDecay(Particle mother,
                           vector<int> ids,
                           CounterPtr& w_incl,
                           bool absolute,
                           Histo1DPtr h_ratio) {
  // w_incl   ... reference to a global weight counter for all leptonic tau decays
  // h_ratio  ... pointer to ratio histo

  // There is no point in looking for decays with less particles than to be analysed
  const Particles& descendants = mother.stableDescendants();
  if (descendants.size() >= ids.size()) {
    const Particles parts = {mother};
    bool decayfound = cascadeContains(parts, ids, absolute, true);
    // Do not increment counters if the specified decay products were not found
    if (decayfound) {
      w_incl->fill(); // the (global) weight counter for leptonic decays
      bool radiative = any(descendants, HasPID(PID::PHOTON));

      // Only fill the histo if there is a radiative decay
      if (radiative) {
        // Iterate over decay products to find photon with 5 MeV energy
        for (const Particle& son : mother.stableDescendants()) {
          if (son.pid() == PID::PHOTON) {
            // Require photons to have at least 5 MeV energy in the rest frame of the tau
            // boosted taus
            if (!mother.momentum().betaVec().isZero()) {
              LorentzTransform cms_boost = LorentzTransform::mkFrameTransformFromBeta(
                  mother.momentum().betaVec());
              if (cms_boost.transform(son.momentum())[0] / MeV > 5.) {
                h_ratio->fill(1);
                break;
              }
            }
            // not boosted taus
            else {
              if (son.momentum()[0] / MeV > 5.) {
                h_ratio->fill(1);
                break;
              }
            }
          }
        } // end loop over decay products
      } // end of radiative
    } // end of decayfound
  } // end of first if
}

private:

/// @name Histograms
/// @{
Histo1DPtr _h_ratio_mu, _h_ratio_el;
Histo1DPtr _h_1prong_pinu, _h_1prong_Kpnu, _h_1prong_Kppinu, _h_1prong_pipinu, _h_1prong_pipipinu,
    _h_1prong_Knpinu;
Histo1DPtr _h_3prong_pipipinu;
Histo1DPtr _h_5prong;
/// @}

CounterPtr _weights_had, _weights_mu, _weights_el;
map<string, vector<int>> decay_pids;

};

RIVET_DECLARE_PLUGIN(PDG_TAUS);

} ```