file src/decay_chain.cpp

[No description available] More…

Namespaces

Name
Gambit
TODO: see if we can use this one:
Gambit::DarkBit
Gambit::DarkBit::DecayChain

Detailed Description

Author: Lars A. Dal (l.a.dal@fys.uio.no)

Date:

  • 2014 Oct, Nov, Dec
  • 2015 Jan

Implementation of classes and functions for decay chain setup Currently only accepts 2-body decays in each step, and assumes that particles decay isotropically in their CM system (like scalars). Does not allow massless particles as intermediate states.


Authors (add name and date if you modify):


Source code

//   GAMBIT: Global and Modular BSM Inference Tool
//   *********************************************
///  \file
///
///  Implementation of classes and functions for decay chain setup
///  Currently only accepts 2-body decays in each step,
///  and assumes that particles decay isotropically in their CM system (like
///  scalars).  Does not allow massless particles as intermediate states.
///
///  *********************************************
///
///  Authors (add name and date if you modify):
///
///  \author Lars A. Dal
///          (l.a.dal@fys.uio.no)
///  \date 2014 Oct, Nov, Dec
///  \date 2015 Jan
///
///  *********************************************

#include "gambit/Elements/gambit_module_headers.hpp"
#include "gambit/DarkBit/DarkBit_rollcall.hpp"

//#define DARKBIT_DEBUG

namespace Gambit
{
  namespace DarkBit
  {
    namespace DecayChain
    {
      using std::ostringstream;
      using std::set;
      using std::endl;
      using std::pair;

      //  *********************************************
      //  3-vector related
      //  *********************************************

      double vec3::length() const
      {
        return sqrt(vals[0]*vals[0]+vals[1]*vals[1]+vals[2]*vals[2]);
      }
      void vec3::normalize()
      {
        double l = length();
        for(int i=0;i<3;i++)
        {
          vals[i] /= l;
        }
      }
      void vec3::normalize(const double len)
      {
        double l = length();
        for(int i=0;i<3;i++)
        {
          vals[i] *= len/l;
        }
      }
      vec3 operator* (double x, const vec3 &y)
      {
        vec3 retVec = y;
        for(int i=0;i<3;i++)
        {
          retVec[i] *= x;
        }
        return retVec;
      }
      vec3 operator* (const vec3 &y, double x)
      {
        vec3 retVec = y;
        for(int i=0;i<3;i++)
        {
          retVec[i] *= x;
        }
        return retVec;
      }
      vec3 operator/ (const vec3 &y, double x)
      {
        vec3 retVec = y;
        for(int i=0;i<3;i++)
        {
          retVec[i] /= x;
        }
        return retVec;
      }
      std::ostream& operator<<(std::ostream& os, const vec3& v)
      {
        os << v[0] << ", " << v[1]  << ", " << v[2];
        return os;
      }
      double dot(const vec3 &a, const vec3 &b)
      {
        return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
      }


      //  *********************************************
      //  4-vector related
      //  *********************************************

      vec4 operator* (double x, const vec4 &y)
      {
        vec4 retVec = y;
        for(int i=0;i<4;i++)
        {
          retVec[i] *= x;
        }
        return retVec;
      }
      vec4 operator* (const vec4 &y, double x)
      {
        vec4 retVec = y;
        for(int i=0;i<4;i++)
        {
          retVec[i] *= x;
        }
        return retVec;
      }
      vec4 operator+ (const vec4 &x, const vec4 &y)
      {
        vec4 retVec = x;
        for(int i=0;i<4;i++)
        {
          retVec[i] += y[i];
        }
        return retVec;
      }
      vec4 operator- (const vec4 &x, const vec4 &y)
      {
        vec4 retVec = x;
        for(int i=0;i<4;i++)
        {
          retVec[i] -= y[i];
        }
        return retVec;
      }
      std::ostream& operator<<(std::ostream& os, const vec4& v)
      {
        os <<
          "(" << v[0] << ", " << v[1]  << ", " << v[2] << ", " << v[3] <<")";
        return os;
      }
      double dot(const vec4 &a, const vec4 &b)
      {
        return a[0]*b[0]-a[1]*b[1]-a[2]*b[2]-a[3]*b[3];
      }
      vec4 Ep4vec(const vec3 p, double m)
      {
        double E = sqrt(dot(p,p)+m*m);
        return vec4(E,p);
      }


      //  *********************************************
      //  4x4 matrix related
      //  *********************************************

      mat4::mat4(
          double v00, double v01, double v02, double v03,
          double v10, double v11, double v12, double v13,
          double v20, double v21, double v22, double v23,
          double v30, double v31, double v32, double v33)
      {
        vals[0][0] = v00;
        vals[0][1] = v01;
        vals[0][2] = v02;
        vals[0][3] = v03;
        vals[1][0] = v10;
        vals[1][1] = v11;
        vals[1][2] = v12;
        vals[1][3] = v13;
        vals[2][0] = v20;
        vals[2][1] = v21;
        vals[2][2] = v22;
        vals[2][3] = v23;
        vals[3][0] = v30;
        vals[3][1] = v31;
        vals[3][2] = v32;
        vals[3][3] = v33;
      }
      mat4::mat4(double v)
      {
        vals[0][0] = v;
        vals[0][1] = v;
        vals[0][2] = v;
        vals[0][3] = v;
        vals[1][0] = v;
        vals[1][1] = v;
        vals[1][2] = v;
        vals[1][3] = v;
        vals[2][0] = v;
        vals[2][1] = v;
        vals[2][2] = v;
        vals[2][3] = v;
        vals[3][0] = v;
        vals[3][1] = v;
        vals[3][2] = v;
        vals[3][3] = v;
      }
      mat4 mat4::identity()
      {
        return mat4(1,0,0,0,
            0,1,0,0,
            0,0,1,0,
            0,0,0,1);
      }
      vec4 operator* (const mat4 &m, const vec4 &v)
      {
        vec4 out(0);
        for(int i=0;i<4;i++)
        {
          for(int j=0;j<4;j++)
          {
            out[i] += m.vals[i][j]*v[j];
          }
        }
        return out;
      }
      mat4 operator* (const mat4 &m1, const mat4 &m2)
      {
        mat4 out(0);
        for(int i=0;i<4;i++)
        {
          for(int j=0;j<4;j++)
          {
            for(int k=0; k<4; k++)
            {
              out.vals[i][j] += m1.vals[i][k]*m2.vals[k][j];
            }
          }
        }
        return out;
      }
      std::ostream& operator<<(std::ostream& os, const mat4& m)
      {
        for(int i=0;i<4;i++)
        {
          for(int j=0;j<4;j++)
          {
            os << m.vals[i][j] << ", ";
          }
          if(i!=3)
            os << endl;
        }
        return os;
      }


      //  *********************************************
      //  Utility functions
      //  *********************************************

      double rand_m1_1()
      {
        return -1.0 + 2.0 * rand_0_1();
      }
      vec3 randOnSphere()
      {
        // Marsaglia 1972 rejection method
        double r1,r2;
        do
        {
          r1 = rand_m1_1();
          r2 = rand_m1_1();
        }
        while(r1*r1+r2*r2 >=1.0);
        vec3 v;
        v[0] = 2.0*r1*sqrt(1-r1*r1-r2*r2);
        v[1] = 2.0*r2*sqrt(1-r1*r1-r2*r2);
        v[2] = 1.0-2.0*(r1*r1+r2*r2);
        return v;
      }
      void lorentzMatrix(const vec3 &beta_xyz, mat4 &mat)
      {
        double b = beta_xyz.length();
        double bm2 = b==0 ? 0 : 1.0/(b*b);
        const double &bx = beta_xyz[0];
        const double &by = beta_xyz[1];
        const double &bz = beta_xyz[2];
        double g = 1.0/sqrt(1-b*b);
        mat =  mat4(    g,      -g*bx,              -g*by,              -g*bz,
            -g*bx,  1+(g-1)*bx*bx*bm2,  (g-1)*bx*by*bm2,    (g-1)*bx*bz*bm2,
            -g*by,  (g-1)*by*bx*bm2,    1+(g-1)*by*by*bm2,  (g-1)*by*bz*bm2,
            -g*bz,  (g-1)*bz*bx*bm2,    (g-1)*bz*by*bm2,    1+(g-1)*bz*bz*bm2);
      }
      void lorentzMatrix(const vec3 &beta_xyz, mat4 &mat, double gamma)
      {
        double b = beta_xyz.length();
        double bm2 = b==0 ? 0 : 1.0/(b*b);
        const double &bx = beta_xyz[0];
        const double &by = beta_xyz[1];
        const double &bz = beta_xyz[2];
        double &g = gamma;
        mat =  mat4(    g,      -g*bx,              -g*by,              -g*bz,
            -g*bx,  1+(g-1)*bx*bx*bm2,  (g-1)*bx*by*bm2,    (g-1)*bx*bz*bm2,
            -g*by,  (g-1)*by*bx*bm2,    1+(g-1)*by*by*bm2,  (g-1)*by*bz*bm2,
            -g*bz,  (g-1)*bz*bx*bm2,    (g-1)*bz*by*bm2,    1+(g-1)*bz*bz*bm2);
      }
      vec4 lorentzBoost(const vec4 &inVec, const vec3 &beta_xyz)
      {
        mat4 lorentz;
        lorentzMatrix(beta_xyz, lorentz);
        return lorentz*inVec;
      }
      vec4 p_parentFrame(const vec4 &inVec, const vec4 &p_parent)
      {
        vec3 beta_xyz = -p_parent.xyz()/p_parent[0];
        return lorentzBoost(inVec, beta_xyz);
      }
      void boostMatrixParentFrame(mat4 &mat, vec4 &p_parent, double m)
      {
        vec3 beta_xyz = -p_parent.xyz()/p_parent[0];
        double gamma = p_parent[0]/m;
        lorentzMatrix(beta_xyz, mat,gamma);
      }
      double invariantMass(const vec4 &a, const vec4 &b)
      {
        vec4 tmp = a+b;
        return sqrt(dot(tmp,tmp));
      }


      //  *********************************************
      //  DecayTableEntry functions
      //  *********************************************

      int DecayTableEntry::findChannelIdx(double pick) const
      {
        if(!randInit)
        {
          piped_warnings.request(LOCAL_INFO,
            "Generating rand table at runtime. This should have happened\n"
            "during initialization, and might not be threadsafe here.");
          generateRandTable();
        }
        vector<double>::const_iterator pos = upper_bound(randLims.begin(),
            randLims.end(),pick);
        return pos - randLims.begin();
      }
      bool DecayTableEntry::randomDecay(const TH_Channel* &decay) const
      {
        if(!hasEnabledDecays()) return false;
        double pick = rand_0_1();
        int idx = findChannelIdx(pick);
        decay = enabledDecays[idx];
        return true;
      }
      void DecayTableEntry::generateRandTable() const
      {
        randLims.clear();
        double tmp=0;
        for(vector<const TH_Channel*>::const_iterator
            it = enabledDecays.begin(); it != enabledDecays.end(); ++it)
        {
          tmp += DecayTable::getWidth(*it)/enabledWidth;
          randLims.push_back(tmp);
        }
        randInit=true;
      }
      void DecayTableEntry::update()
      {
        totalWidth = 0;
        enabledWidth = 0;
        for(vector<const TH_Channel*>::const_iterator
            it = enabledDecays.begin(); it != enabledDecays.end(); ++it)
        {
          enabledWidth += DecayTable::getWidth(*it);
          totalWidth   += DecayTable::getWidth(*it);
        }
        for(vector<const TH_Channel*>::const_iterator
            it = disabledDecays.begin(); it != disabledDecays.end(); ++it)
        {
          totalWidth   += DecayTable::getWidth(*it);
        }
        totalWidth += invisibleWidth;
        if(randInit) generateRandTable();
      }
      bool DecayTableEntry::isEnabled(const TH_Channel *in) const
      {
        for(vector<const TH_Channel*>::const_iterator
            it = enabledDecays.begin(); it != enabledDecays.end(); ++it)
        {
          if((*it) == in) return true;
        }
        return false;
      }
      bool DecayTableEntry::isDisabled(const TH_Channel *in) const
      {
        for(vector<const TH_Channel*>::const_iterator
            it = disabledDecays.begin(); it != disabledDecays.end(); ++it)
        {
          if((*it) == in) return true;
        }
        return false;
      }
      bool DecayTableEntry::isRegistered (const TH_Channel *in) const
      {
        if(isEnabled(in)) return true;
        if(isDisabled(in)) return true;
        return false;
      }
      void DecayTableEntry::addChannel(const TH_Channel *in)
      {
        if(in->nFinalStates > 2)
        {
          piped_warnings.request(LOCAL_INFO,
            "Trying to add decay with >2 final states to DecayTableEntry.\n"
            "Channel added as disabled.");
          addDisabled(in);
          return;
        }
        enabledDecays.push_back(in);
        totalWidth   += DecayTable::getWidth(in);
        enabledWidth += DecayTable::getWidth(in);
      }
      void DecayTableEntry::addDisabled(const TH_Channel *in)
      {
        disabledDecays.push_back(in);
        totalWidth   += DecayTable::getWidth(in);
      }
      void DecayTableEntry::setInvisibleWidth(double width)
      {
        invisibleWidth = width;
        totalWidth  += width;
      }
      bool DecayTableEntry::enableDecay(const TH_Channel *in)
      {
        // N>2 body decays currently not possible.
        if(in->nFinalStates > 2)
        {
          piped_warnings.request(LOCAL_INFO,
            "Trying to enable decay with >2 final states in decay chain.\n"
            "Request ignored.");
          return false;
        }
        bool found = false;
        for(vector<const TH_Channel*>::iterator
            it = disabledDecays.begin(); it != disabledDecays.end(); ++it)
        {
          if((*it) == in)
          {
            found = true;
            disabledDecays.erase(it);
          }
        }
        if(!found) return false;
        enabledDecays.push_back(in);
        enabledWidth += DecayTable::getWidth(in);
        // Re-generate Monte Carlo table if necessary
        if(randInit) generateRandTable();
        return true;
      }
      bool DecayTableEntry::disableDecay(const TH_Channel *in)
      {
        bool found = false;
        for(vector<const TH_Channel*>::iterator it = enabledDecays.begin();
            it != enabledDecays.end(); ++it)
        {
          if((*it) == in)
          {
            found = true;
            enabledDecays.erase(it);
          }
        }
        if(!found) return false;
        disabledDecays.push_back(in);
        enabledWidth -= DecayTable::getWidth(in);
        // Re-generate Monte Carlo table if necessary
        if(randInit) generateRandTable();
        return true;
      }
      double DecayTableEntry::getEnabledBranching() const
      {
        return hasEnabledDecays() ? enabledWidth/getTotalWidth() : 0.0;
      }
      void DecayTableEntry::forceTotalWidth(bool enabled, double width)
      {
        useForcedTotalWidth = enabled;
        forcedTotalWidth = width;
      }
      double DecayTableEntry::getTotalWidth() const
      {
        return useForcedTotalWidth ? forcedTotalWidth : totalWidth;
      }
      bool DecayTableEntry::hasEnabledDecays() const
      {
        return (enabledDecays.size()>0);
      }

      //  *********************************************
      //  DecayTable functions
      //  *********************************************

      DecayTable::DecayTable(const TH_ProcessCatalog &cat,
          const SimYieldTable &tab, set<string> disabledList)
      {
#ifdef DARKBIT_DEBUG
        std::cout << "Importing CascadeMC DecayTable from process catalog..." << std::endl;
#endif
        set<string> finalStates;
        // Register all decaying particles and their decays
        for(vector<TH_Process>::const_iterator it = cat.processList.begin();
            it != cat.processList.end(); ++it)
        {
          // Only interested in decay processes
          if(it->isAnnihilation) continue;

          string pID = it->particle1ID;
          double m = cat.getParticleProperty(pID).mass;
          bool stable = ((it->channelList).size()<1);
#ifdef DARKBIT_DEBUG
          std::cout << "Importing " << pID << std::endl;
#endif
          if(disabledList.count(pID)==1) stable = true;
          // If tabulated spectra exist for decays of this particle, consider
          // it stable for the purpose of decay chain generation.
          if(tab.hasAnyChannel(pID)) stable = true;
//          if(!stable and (width <=0.0))
//          {
//             piped_warnings.request(LOCAL_INFO,
//               "Unstable particle "+pID+" with zero width in decay table. Treating it as stable in cascade decays.");
//             stable = true;
//          }
          // Create DecayTableEntry and insert decay channels
          DecayTableEntry entry(pID,m,stable);
          for(vector<TH_Channel>::const_iterator it2 = (
                it->channelList).begin(); it2 != (it->channelList).end(); ++it2)
          {
            // N>2 body decays currently not supported, but should be included
            // as disabled to get the correct total width.
            if((it2->nFinalStates) > 2)
            {
              entry.addDisabled(&(*it2));
            }
            else
            {
              entry.addChannel(&(*it2));
            }
            for(vector<string>::const_iterator
                it3 = (it2->finalStateIDs).begin();
                it3 != (it2->finalStateIDs).end(); ++it3)
            {
              finalStates.insert(*it3);
            }
          }
          entry.setInvisibleWidth(it->genRateMisc->bind()->eval());
          if(!stable and entry.enabledDecays.size() == 0)
          {
            piped_warnings.request(LOCAL_INFO,
              "Unstable particle "+pID+" has no available decay channels. Treating it as stable in cascade decays.");
            entry.stable = true;
          }
          addEntry(pID,entry);
        }
        // Flag channels where all final final states are stable as endpoints.
        // Loop over all particles
        for(unordered_map<string,DecayTableEntry>::iterator it = table.begin();
            it != table.end(); ++it)
        {
          // Loop over all decays
          for(vector<const TH_Channel*>::const_iterator
              it2 = (it->second.enabledDecays).begin();
              it2 != (it->second.enabledDecays).end(); ++it2)
          {
            bool isEndpoint = true;
            // Loop over all final states
            for(vector<string>::const_iterator
                it3 = ((*it2)->finalStateIDs).begin();
                it3 != ((*it2)->finalStateIDs).end(); ++it3)
            {
              // All (and only) decaying particles are registered so far. If
              // particle is registered, it's not stable.
              if(hasEntry(*it3))
              {
                if(!table[*it3].stable)
                {
                  isEndpoint = false;
                  break;
                }
              }
            }
            it->second.endpointFlags[*it2] = isEndpoint;
          }
          it->second.generateRandTable();
        }
        // Iterate over final states and register particles that have not
        // already been registered (because they are considered stable).
        for(set<string>::iterator it = finalStates.begin();
            it != finalStates.end(); ++it)
        {
          if(!hasEntry(*it))
          {
            double m = cat.getParticleProperty(*it).mass;
            addEntry(*it,m,true);
          }
        }
#ifdef DARKBIT_DEBUG
        std::cout << "...done" << std::endl;
#endif
      }
      bool DecayTable::hasEntry(string index) const
      {
        return table.find(index) != table.end();
      }
      void DecayTable::addEntry(string pID, double m, bool stable)
      {
        table.insert (
            pair<string,DecayTableEntry>(pID,DecayTableEntry(pID,m,stable)) );
      }
      void DecayTable::addEntry(string pID, DecayTableEntry entry)
      {
        table.insert ( pair<string,DecayTableEntry>(pID,entry) );
      }
      bool DecayTable::randomDecay(string pID, const TH_Channel* &decay) const
      {
        bool ans=false;
        try
        {
          ans=(table.at(pID)).randomDecay(decay);
        }
        catch (std::out_of_range& e)
        {
          throw(Piped_exceptions::description(LOCAL_INFO,
                "No partcile "+pID+" in decay table."));
        }
        return ans;
      }
      const DecayTableEntry& DecayTable::operator[](string i) const
      {
        const DecayTableEntry *ent = NULL;
        try
        {
          ent= &table.at(i);
        }
        catch (std::out_of_range& e)
        {
          throw(Piped_exceptions::description(LOCAL_INFO,
                "No partcile "+i+" in decay table."));
        }
        return *ent;
      }
      double DecayTable::getWidth(const TH_Channel *ch)
      {
        return ch->genRate->bind()->eval();
      }
      void DecayTable::printTable() const
      {
#ifdef DARKBIT_DEBUG
        std::cout << std::endl;
        std::cout << "***********************" << endl;
        std::cout << "CMC DecayTable printout" << endl;
        std::cout << "***********************" << endl;
        std::cout << std::endl;
        for(unordered_map<string,DecayTableEntry>::const_iterator
            it = table.begin(); it != table.end(); ++it)
        {
          std::cout << "Particle: " <<(it->first) << endl;
          std::cout << "Set stable: " << (it->second).stable << endl;
          std::cout << "Mass: " <<(it->second).m << endl;
          std::cout << "Total width: " << (it->second.getTotalWidth())<< endl;
          std::cout << "Enabled branching ratio: "
            << (it->second.getEnabledBranching()) << endl;
          std::cout << "Enabled decays:" << endl;
          for(vector<const TH_Channel*>::const_iterator
              it2 = (it->second.enabledDecays).begin();
              it2 != (it->second.enabledDecays).end(); ++it2)
          {
            std::cout << "  ";
            for(vector<string>::const_iterator
                it3 = ((*it2)->finalStateIDs).begin();
                it3 != ((*it2)->finalStateIDs).end(); ++it3)
            {
              std::cout << *it3 << ", ";
            }
            std::cout << "Width: " << getWidth(*it2) << endl;
          }
          std::cout << "Disabled decays:" << endl;
          for(vector<const TH_Channel*>::const_iterator
              it2 = (it->second.disabledDecays).begin();
              it2 != (it->second.disabledDecays).end(); ++it2)
          {
            std::cout << "  ";
            for(vector<string>::const_iterator
                it3 = ((*it2)->finalStateIDs).begin();
                it3 != ((*it2)->finalStateIDs).end(); ++it3)
            {
              std::cout << *it3 << ", ";
            }
            std::cout << "Width: " << getWidth(*it2) << endl;
          }
          std::cout << std::endl;
        }
#endif
      }


      //  *********************************************
      //  ChainParticle functions
      //  *********************************************

      ChainParticle::ChainParticle(
          vec3 ipLab, const DecayTable *dc, string pID) :
        m((*dc)[pID].m), weight(1), decayTable(dc), pID(pID),
        chainGeneration(0), abortedDecay(false), isEndpoint(false),
        nChildren(0), parent(NULL)
      {
        p_parent=Ep4vec(ipLab,m);
        boostMatrixParentFrame(boostToParentFrame,p_parent,m);
        boostToLabFrame = boostToParentFrame;
      }
      void ChainParticle::generateDecayChainMC(int maxSteps, double Emin)
      {
        if(nChildren!=0)
        {
          piped_warnings.request(LOCAL_INFO,
            "Overwriting existing decay in decay chain.");
          cutChain();
        }
        // Stable particles flagged as endpoints
        if((*decayTable)[pID].stable)
        {
          isEndpoint = true;
        }
        // Check whether or not to proceed with decay
        else if( ((maxSteps < 0) or (int(chainGeneration) < maxSteps))
            and ((Emin < 0) or (E_Lab()> Emin)) )
        {
          const TH_Channel *chn;
          bool canDecay = decayTable->randomDecay(pID, chn);
          if(!canDecay)
          {
            piped_warnings.request(LOCAL_INFO,
              "Unable to pick allowed decay for "+ pID+". Keeping particle stable.");
            abortedDecay = true;
            return;
          }
          // Only 2-body decays are currently allowed
          if((chn->nFinalStates) != 2)
          {
            string err;
            err = "Invalid decay channel in decay table.\n";
            err+= "N!=2 body decays are currently not supported in cascade decays.\n";
            throw(Piped_exceptions::description(LOCAL_INFO,err));
            return;
          }
          nChildren = 2; // chn->nFinalStates;
          // Kinematics for 2-body decays
          double m1 = (*decayTable)[(chn->finalStateIDs)[0]].m;
          double m2 = (*decayTable)[(chn->finalStateIDs)[1]].m;
          if(m1+m2>m)
          {
            ostringstream err;
            err <<
              "Kinematically impossible decay in decay chain:\n" <<
              pID << "-> " <<
              ((chn->finalStateIDs)[0]) << ", " << ((chn->finalStateIDs)[1]) << "\n" <<
              "Please check your process catalog." << endl;
            err << "Relevant particle masses: " << m << " -> " << m1 << " + " << m2;
            throw(Piped_exceptions::description(LOCAL_INFO, err.str()));
          }
          const double &Etot = m;
          double E1 = 0.5*(Etot*Etot+m1*m1-m2*m2)/Etot;
          double E2 = Etot-E1;
          double abs_p = sqrt(E1*E1-m1*m1);
          // Assume isotropic decay in CM frame (no spin correlations).
          vec3 dir = randOnSphere();
          vec4 p1(E1, abs_p*dir);
          vec4 p2(E2,-abs_p*dir);
          // Weight from not including all possible decay channels
          double wt = weight*(*decayTable)[pID].getEnabledBranching();
          children.push_back(new ChainParticle(p1, m1, wt, decayTable, this,
                chainGeneration+1, chn->finalStateIDs[0]));
          children.push_back(new ChainParticle(p2, m2, wt, decayTable, this,
                chainGeneration+1, chn->finalStateIDs[1]));
          // Reached chain endpoint. Don't attempt further decays
          if((*decayTable)[pID].endpointFlags.at(chn))
          {
            isEndpoint = true;
          }
          // Continue chain from child links.
          else
          {
            for(int i=0;i<nChildren;i++)
            {
              children[i]->generateDecayChainMC(maxSteps, Emin);
            }
          }
        }
        else
        {
          abortedDecay = true;
        }
      }
      void ChainParticle::reDrawAngles()
      {
        if(nChildren==2)
        {
          double m1 = children[0]->m;
          vec3  dir = randOnSphere();
          double E1 = children[0]->p_parent[0];
          double E2 = children[1]->p_parent[0];
          double abs_p = sqrt(E1*E1-m1*m1);
          vec4 p1(E1, abs_p*dir);
          vec4 p2(E2,-abs_p*dir);
          children[0]->update(p1);
          children[1]->update(p2);
          children[0]->reDrawAngles();
          children[1]->reDrawAngles();
        }
      }
      void ChainParticle::cutChain()
      {
        for(int i=0;i<nChildren; i++) delete children[i];
        children.clear();
        nChildren = 0;
      }
      vec4 ChainParticle::p_to_Lab(const vec4 &p) const
      {
        return boostToLabFrame*p;
      }
      vec4 ChainParticle::p_Lab() const
      {
        if(chainGeneration==0) return p_parent;
        return parent->boostToLabFrame*p_parent;
      }
      double ChainParticle::E_Lab() const
      {
        if(chainGeneration==0) return p_parent[0];
        double E = 0;
        for(int j=0;j<4;j++)
        {
          E += (parent->boostToLabFrame).vals[0][j]*p_parent[j];
        }
        return E;
      }
      void ChainParticle::collectEndpointStates(vector<const ChainParticle*>
          &endpointStates, bool includeAborted, string ipID) const
      {
        if(abortedDecay)
        {
          if(includeAborted && ((ipID=="") || (ipID==pID)))
            endpointStates.push_back(this);
        }
        else
        {
          if(nChildren!=0 and !isEndpoint)
          {
            for(vector<ChainParticle*>::const_iterator it=children.begin();
                it!=children.end(); ++it)
            {
              (*it)->collectEndpointStates(endpointStates,includeAborted,pID);
            }
          }
          else if((ipID=="") or (ipID==pID) or isEndpoint)
          {
            endpointStates.push_back(this);
          }
        }
      }
      const ChainParticle* ChainParticle::operator[](int i) const
      {
        if(i<nChildren)
          return children[i];
        else
        {
          throw(Piped_exceptions::description(LOCAL_INFO,
                "Trying to access non-existing decay chain entry."));
          return this;
        }
      }
      const ChainParticle* ChainParticle::getParent() const
      {
        return parent;
      }
      double ChainParticle::E_parentFrame() const
      {
        return p_parent[0];
      }
      void ChainParticle::printChain() const
      {
        std::cout << "*********************" << endl;
        std::cout << "Decay chain printout:" << endl;
        std::cout << "---------------------" << endl;
        std::cout << "Generation " << chainGeneration << ":" << endl;
        std::cout << "0  " << pID << ", p = " << p_Lab() <<
          ", Weight: " << weight  << endl;
        std::cout << "---------------------" << endl;
        if(nChildren>0)
        {
          bool run = false;
          int gen = chainGeneration+1;
          do
          {
            std::cout << "Generation " << gen <<":" << endl;
            run= false;
            for(int i=0;i<nChildren;i++)
            {
              vector<int> ancestry;
              ancestry.push_back(0);
              ancestry.push_back(i);
              bool more = children[i]->printChain(gen,ancestry);
              run = more || run;
            }
            gen++;
            std::cout << "---------------------" << endl;
          }
          while(run);
        }
      }
      bool ChainParticle::printChain(int generation, vector<int> ancestry) const
      {
        if(generation < chainGeneration) return false;
        if(generation > chainGeneration)
        {
          bool more = false;
          for(int i=0;i<nChildren;i++)
          {
            vector<int> ancestry2 = ancestry;
            ancestry2.push_back(i);
            if(children[i]->printChain(generation,ancestry2)) more = true;
          }
          return more;
        }
        for(vector<int>::const_iterator it=ancestry.begin();
            it!=ancestry.end(); ++it)
        {
          std::cout << *it << "  ";
        }
        std::cout << pID  << ", p = " << p_Lab() << ", Weight: " << weight  << endl;
        if(nChildren>0) return true;
        return false;
      }
      void ChainParticle::getBoost(double& gamma, double& beta) const
      {
        const mat4& b=boostToLabFrame;
        gamma = b.vals[0][0];
        beta = sqrt(b.vals[0][1]*b.vals[0][1]+b.vals[0][2]*b.vals[0][2]+
            b.vals[0][3]*b.vals[0][3])/gamma;
      }
      ChainParticle::~ChainParticle()
      {
        for(int i=0;i<nChildren; i++) delete children[i];
      }
      void ChainParticle::update(vec4 &ip_parent)
      {
        p_parent = ip_parent;
        boostMatrixParentFrame(boostToParentFrame,p_parent,m);
        boostToLabFrame = parent->boostToLabFrame*boostToParentFrame;
      }
      ChainParticle::ChainParticle(const vec4 &pp, double m, double weight,
          const DecayTable *dc, ChainParticle *parent, int chainGeneration,
          string pID) :
        m(m), weight(weight), decayTable(dc), p_parent(pp), pID(pID),
        chainGeneration(chainGeneration), abortedDecay(false),
        isEndpoint(false), nChildren(0), parent(parent)
      {
        boostMatrixParentFrame(boostToParentFrame,p_parent,m);
        boostToLabFrame = parent->boostToLabFrame*boostToParentFrame;
      }

    } // namespace DecayChain
  } // namespace DarkBit
} // namespace Gambit

Updated on 2023-06-26 at 21:36:55 +0000