Source code for theory.branch

"""
.. module:: theory.branch
   :synopsis: Module holding the branch class and methods.
        
.. moduleauthor:: Andre Lessa <lessa.a.p@gmail.com>
        
"""

from smodels.theory.particleNames import simParticles, elementsInStr
from smodels.tools.physicsUnits import fb
import logging
from smodels.particles import rEven, ptcDic
import sys

logger = logging.getLogger(__name__)


[docs]class Branch(object): """ An instance of this class represents a branch. A branch-element can be constructed from a string (e.g., ('[b,b],[W]'). :ivar masses: list of masses for the intermediate states :ivar particles: list of particles (strings) for the final states :ivar momID: PDG id for the primary (intermediate state) mother :ivar daughterID: PDG id for the last intermediate state :ivar maxWeight: weight of the branch (XSection object) """ def __init__(self, info=None): """ Initializes the branch. If info is defined, tries to generate the branch using it. :parameter info: string describing the branch in bracket notation (e.g. [[e+],[jet]]) """ self.masses = [] self.particles = [] self.momID = None self.daughterID = None self.maxWeight = None if type(info) == type(str()): branch = elementsInStr(info) if not branch or len(branch) > 1: logger.error("Wrong input string " + info) sys.exit() else: branch = branch[0] vertices = elementsInStr(branch[1:-1]) for vertex in vertices: ptcs = vertex[1:-1].split(',') # Syntax check: for ptc in ptcs: if not ptc in rEven.values() \ and not ptc in ptcDic: logger.error("Unknown particle. Add " + ptc + " to smodels/particle.py") sys.exit() self.particles.append(ptcs) def __str__(self): """ Create the branch bracket notation string, e.g. [[e+],[jet]]. :returns: string representation of the branch (in bracket notation) """ st = str(self.particles).replace("'", "") st = st.replace(" ", "") return st def __eq__(self, other): """ Check if branches are equal, allowing for inclusive particle labels. :parameter other: branch to be compared (Branch object) :returns: True if branches are equal (particles and masses match); False otherwise. """ return self.isEqual(other) def __ne__(self, other): """ Check if branches are different, allowing for inclusive particle labels. :parameter other: branch to be compared (Branch object) :returns: False if branches are equal (particles and masses match); True otherwise. """ return not self.isEqual(other)
[docs] def isEqual(self, other, useDict=True): """ Compares two branches. If particles are similar and masses are equal, return True. Otherwise, return False. :parameter other: branch to be compared (Branch object) :parameter useDict: if True, allow for inclusive particle labels :returns: True if branches are equal (particles and masses match); False otherwise. """ if type (other) != type(self): return False if not simParticles(self.particles, other.particles, useDict): return False if self.masses != other.masses: return False return True
[docs] def copy(self): """ Generate an independent copy of self. Faster than deepcopy. :returns: Branch object """ newbranch = Branch() newbranch.masses = self.masses[:] newbranch.particles = self.particles[:] newbranch.momID = self.momID newbranch.daughterID = self.daughterID if not self.maxWeight is None: newbranch.maxWeight = self.maxWeight.copy() return newbranch
[docs] def getLength(self): """ Returns the branch length (= number of R-odd masses). :returns: length of branch (number of cascade decay steps) """ return len(self.masses)
def _addDecay(self, br, massDictionary): """ Generate a new branch adding a 1-step cascade decay This is described by the br object, with particle masses given by massDictionary. :parameter br: branching ratio object (see pyslha). Contains information about the decay. :parameter massDictionary: dictionary containing the masses for all intermediate states. :returns: extended branch (Branch object). False if there was an error. """ newBranch = self.copy() newparticles = [] newmass = [] newBranch.daughterID = None for partID in br.ids: # Add R-even particles to final state if partID in rEven: newparticles.append(rEven[partID]) else: # Add masses of non R-even particles to mass vector newmass.append(massDictionary[partID]) newBranch.daughterID = partID if len(newmass) > 1: logger.warning("Multiple R-odd particles in the final state: " + str(br.ids)) return False if newparticles: newBranch.particles.append(newparticles) if newmass: newBranch.masses.append(newmass[0]) if not self.maxWeight is None: newBranch.maxWeight = self.maxWeight * br.br return newBranch
[docs] def decayDaughter(self, brDictionary, massDictionary): """ Generate a list of all new branches generated by the 1-step cascade decay of the current branch daughter. :parameter brDictionary: dictionary with the decay information for all intermediate states (values are br objects, see pyslha) :parameter massDictionary: dictionary containing the masses for all intermediate states. :returns: list of extended branches (Branch objects). Empty list if daughter is stable or if daughterID was not defined. """ if not self.daughterID: # Do nothing if there is no R-odd daughter (relevant for RPV decays # of the LSP) return [] # List of possible decays (brs) for R-odd daughter in branch brs = brDictionary[self.daughterID] if len(brs) == 0: # Daughter is stable, there are no new branches return [] newBranches = [] for br in brs: # Generate a new branch for each possible decay newBranches.append(self._addDecay(br, massDictionary)) return newBranches
[docs]def decayBranches(branchList, brDictionary, massDictionary, sigcut=0. *fb): """ Decay all branches from branchList until all unstable intermediate states have decayed. :parameter branchList: list of Branch() objects containing the initial mothers :parameter brDictionary: dictionary with the decay information for all intermediate states (values are br objects, see pyslha) :parameter massDictionary: dictionary containing the masses for all intermediate states. :parameter sigcut: minimum sigma*BR to be generated, by default sigcut = 0. (all branches are kept) :returns: list of branches (Branch objects) """ finalBranchList = [] while branchList: # Store branches after adding one step cascade decay newBranchList = [] for inbranch in branchList: if sigcut.asNumber() > 0. and inbranch.maxWeight < sigcut: # Remove the branches above sigcut and with length > topmax continue # Add all possible decays of the R-odd daughter to the original # branch (if any) newBranches = inbranch.decayDaughter(brDictionary, massDictionary) if newBranches: # New branches were generated, add them for next iteration newBranchList.extend(newBranches) else: # All particles have already decayed, store final branch finalBranchList.append(inbranch) # Use new branches (if any) for next iteration step branchList = newBranchList #Sort list by initial branch PID: finalBranchList = sorted(finalBranchList, key=lambda branch: branch.momID) return finalBranchList