Source code for tools.particlesLoader

#!/usr/bin/env python3

"""
.. module:: particlesLoader
   :synopsis: Loads the file defining the list of BSM particles to be used.

.. moduleauthor:: Andre Lessa <lessa.a.p@gmail.com>

"""

import os
import sys
from smodels.base.exceptions import SModelSBaseError as SModelSError
from smodels.base.smodelsLogging import logger
from smodels.base.particle import Particle
from smodels.installation import installDirectory
from smodels.share.models.SMparticles import SMList
from importlib import import_module



[docs]def getParticlesFromSLHA(slhaData): """ Defines BSM particles from the QNUMBERS blocks in the slhafile. :param slhaData: Path to the SLHA file or the a string with the SLHA data :return: List with Particle objects """ # Create a list of SM PDGs, so if a QNUMBERS block for a SM particle # is present, it will be ignored. SMpdgs = set() for ptc in SMList: if isinstance(ptc.pdg,(int,float)): SMpdgs.add(int(abs(ptc.pdg))) else: for pdg in ptc.pdg: SMpdgs.add(int(abs(pdg))) SMpdgs = list(SMpdgs) if 'block qnumbers' in slhaData.lower(): data = slhaData[:] else: filename = slhaData #If file does not exist, check if it is in any of the default folders: checkDirs = [os.path.join(installDirectory(), "smodels", "share", "models"), installDirectory(), os.path.join(installDirectory(), "smodels")] if not os.path.isfile(slhaData): for dirPath in checkDirs: if os.path.isfile(os.path.join(dirPath, slhaData)): filename = os.path.join(dirPath, slhaData) break if not os.path.isfile(filename): logger.error(f"Model file {slhaData} not found.") raise SModelSError() logger.debug(f"Trying to define BSM particles from SLHA input file {filename}") #Read file and extract blocks: with open(filename, 'r') as f: data = f.read() data = data.lower() qnumberBlocks = [] qBlock = False for l in data.splitlines(): l = l.strip() # Ignore empty lines and comments if not l or l.startswith('#'): continue # Beginning of QNUMBERS block if l.startswith('block qnumbers'): qBlock = True qnumberBlocks.append([l]) continue # If any other block is starting set qBlock to False elif l.startswith('block'): qBlock = False continue # If a cross-section or decay block is starting set qBlock to False elif l.startswith('xsection') or l.startswith('decay'): qBlock = False continue # If current block is not a qnumbers block, skip if not qBlock: continue # Add line to qnumbers block qnumberBlocks[-1].append(l) if not qnumberBlocks: logger.error(f"No QNUMBERS blocks were found") raise SModelSError() #Build list of BSM particles: BSMList = [] for b in qnumberBlocks: headerInfo = [x for x in b[0].replace('block','').replace('qnumbers','').split() if x != '#'] if headerInfo[0].replace('-','').replace('+','').isdigit(): pdg = eval(headerInfo[0]) else: logger.error(f"Error obtaining PDG number from QNUMBERS block:\n {b} \n") raise SModelSError() if any(p.pdg == pdg for p in BSMList): logger.warning("Particle with pdg %i appears multiple times in QNUMBERS blocks" %pdg) continue if len(headerInfo) > 1: label = headerInfo[1].strip() else: label = str(pdg) logger.debug("Could not find label for particle %i, will use its PDG number" %pdg) try: numbers = [l[:l.find('#')].lstrip().split() for l in b[1:]] numbers = dict([x for x in numbers if x]) numbers = dict([[eval(x),eval(y)] for x,y in numbers.items()]) except: logger.error(f"Error reading quantum numbers from block: \n {b} \n") continue if any(not x in numbers for x in [1,2,3]): logger.error(f"Missing quantum numbers in block:\n {b}\n") continue # Ignore SM blocks: if abs(pdg) in SMpdgs: continue # If it is not a SM particle, assume it is a BSM particle newParticle = Particle(isSM=False, label=label, pdg=pdg, eCharge=numbers[1]/3., colordim=numbers[3], spin=(numbers[2]-1.)/2.) BSMList.append(newParticle) if numbers[4]: # Particle is not its own anti-particle newParticleC = newParticle.chargeConjugate() if any(p.pdg == newParticleC.pdg for p in BSMList): continue BSMList.append(newParticleC) return BSMList
[docs]def getParticlesFromLHE(lhefile): """ Defines BSM particles from the QNUMBERS blocks in the <slha> block contained in the lhefile. :param lhefile: Path to the LHE file containing a <slha> block :return: List with Particle objects """ checkDirs = [os.path.join(installDirectory(), "smodels", "share", "models"), installDirectory(), os.path.join(installDirectory(), "smodels")] filename = lhefile #If file does not exist, check if it is in any of the default folders: if not os.path.isfile(lhefile): for dirPath in checkDirs: if os.path.isfile(os.path.join(dirPath, lhefile)): filename = os.path.join(dirPath, lhefile) break if not os.path.isfile(filename): logger.error(f"Model file {lhefile} not found.") raise SModelSError() logger.debug(f"Trying to define BSM particles from LHE input file {filename}") import re import xml.etree.ElementTree as ET with open(filename,'r') as f: lhe_text = f.read() # Locate the first <slha>...</slha> block m = re.search( r'<slha\b[^>]*>(?P<inner>.*?)</slha>', lhe_text, flags=re.DOTALL|re.IGNORECASE ) if not m: raise ValueError("No <slha>...</slha> block found.") inner = m.group('inner') # Wrap the inner content to parse the mixed text+elements safely wrapper_xml = f'<__wrap__>{inner}</__wrap__>' # Default parser is fine; we don't need to preserve comments as "tags" wrapper = ET.fromstring(wrapper_xml) slhaStr = wrapper.text if not slhaStr or 'block qnumbers' not in slhaStr.lower(): raise ValueError(f"No qnumbers block found in {lhefile}.") BSMList = getParticlesFromSLHA(slhaStr) return BSMList
[docs]def getParticlesFromModule(modelFile): """ Reads the python model file and retrieves the list of BSM particles (BSMList) :param modelFile: Name/path to the python module containing the BSM particle definitions :return: a list of Particle objects """ fulldir = os.path.join(installDirectory(), "smodels", "share", "models") sys.path.insert(0, installDirectory()) sys.path.insert(0, os.path.join(installDirectory(), "smodels")) sys.path.insert(0, fulldir) sys.path.insert(0, ".") logger.debug(f"Trying to load model file: {modelFile}") fname = modelFile[:] if "/" in fname: import shutil filename = os.path.basename(fname) if not os.path.exists ( filename ) or not os.path.samefile ( fname, filename ): shutil.copy(fname, filename) else: filename = fname if filename.endswith(".py"): importName = filename[:-3] else: importName = filename pM=import_module(importName, package='smodels') logger.debug(f"Found model file at {pM.__file__}") if filename != fname: os.remove(filename) BSMList = pM.BSMList return BSMList
[docs]def load(): from smodels.base.runtime import modelFile BSMList = None try: BSMList = getParticlesFromModule(modelFile) #If failed, assume the input is an SLHA or LHE file: except (ImportError, AttributeError, ValueError, SModelSError): checkDirs = [os.path.join(installDirectory(), "smodels", "share", "models"), installDirectory(), os.path.join(installDirectory(), "smodels")] filename = modelFile #If file does not exist, check if it is in any of the default folders: if not os.path.isfile(modelFile): for dirPath in checkDirs: if os.path.isfile(os.path.join(dirPath, modelFile)): filename = os.path.join(dirPath, modelFile) break if not os.path.isfile(filename): logger.error(f"Model file {modelFile} not found.") raise SModelSError() with open(filename,'r') as f: data = f.read().lower() if ('<slha>' in data) and ('</slha>' in data): try: BSMList = getParticlesFromLHE(modelFile) except (ImportError, AttributeError, ValueError, SModelSError): pass elif 'block qnumbers' in data: try: BSMList = getParticlesFromSLHA(modelFile) except (ImportError, AttributeError, ValueError, SModelSError): pass if BSMList is None: logger.error(f"Could not load input model from {modelFile}. The file should be either a python module with particle definitions, a SLHA file with QNUMBERS blocks or a LHE file with a <slha> block.") raise SModelSError() return BSMList