Source code for bsmart.scripts.prepare_model

#!/usr/bin/env python3

"""
Script to prepare a model using SARAH for use with selected tools, including SPheno, MicrOmegas, HiggsBounds, HiggsSignals, HiggsTools, flavio, MultiNest and BSMArt

2025/01/29
"""


import os
from datetime import datetime
from socketserver import ThreadingUnixStreamServer
import sys

import shutil
import json
import collections

import argparse

import subprocess

import logging

try:
    from bsmart.scripts import prepare_bsmart as prepareBSMArt
except ImportError:
    try:
        import prepareBSMArt
    except:
        print('Could not import prepareBSMArt, necessary for creating the BSMArt template!')
        sys.exit(1)


log = None

[docs] def get_lib_path(basedir): dirs_to_try=[os.path.join(basedir,'lib64'),os.path.join(basedir,'lib')] pylibdir='' for trydir in dirs_to_try: try: if os.path.exists(trydir): return trydir except: pass raise NameError('No library directory?')
[docs] def shell_command(command_to_execute,working_dir,errors_are_fatal=False,log_everything=False): global log try: command_line = subprocess.Popen(command_to_execute,shell=True, cwd=working_dir,stdout=subprocess.PIPE,stderr=subprocess.PIPE) out, err = command_line.communicate() except Exception as e: raise NameError(e) res = out.decode().strip() errmess=err.decode().strip() returnval=command_line.returncode #log.debug(out.decode("utf-8").strip()) if returnval > 0: ## failed to run at all if err !=b'': log.error('Error message(s) from '+command_to_execute+': '+errmess) if errors_are_fatal: raise NameError(command_to_execute + ' failed: '+errmess) else: if err != b'': log.debug('Warning messages from '+command_to_execute+': '+errmess) if log_everything: log.info('Command '+str(command_to_execute)+', produced: \n'+str(res)) else: log.debug('Command '+str(command_to_execute)+', produced: \n'+str(res)) return res
[docs] def main(): global log info_message='Please specify the tool(s) you want to use. SPheno will always be included.\n Valid options are: All, MicrOmegas, HiggsBounds, HiggsSignals, BSMArt.' parser = argparse.ArgumentParser( description=info_message) parser.add_argument('ModelName', metavar='<Model Name>', type=str, help='Input Model name') parser.add_argument("--All", help="Setup all tools", action="store_true") parser.add_argument("--HiggsBounds", help="Configure for HiggsBounds", action="store_true") parser.add_argument("--HiggsSignals", help="Configure for HiggsSignals", action="store_true") parser.add_argument("--HiggsTools", help="Configure for HiggsTools", action="store_true") parser.add_argument("--MicrOMEGAs", help="Configure for MicrOMEGAs", action="store_true") parser.add_argument("--BSMArt", help="Configure for BSMArt", action="store_true") parser.add_argument("--flavio", help="Configure for flavio", action="store_true") parser.add_argument("--VevaciousPlusPlus", help="Configure Vevacious++", action="store_true") parser.add_argument("--UFO", help="Create UFO file", action="store_true") parser.add_argument("--anyBSM",help="Configure for anyBSM", action="store_true") parser.add_argument("--FlexibleSUSY",help="Model name for FlexibleSUSY",action="store",default='',type=str) parser.add_argument("--SPheno",help="Skip SPheno", action="store_true",default=False) parser.add_argument("--Options", help="Options to pass to MakeSPheno",action="store", default='',type=str) parser.add_argument("--MOOptions", help="Options to pass to MakeCHep",action="store", default='',type=str) parser.add_argument("--Init", help="Extra commands for the Mathematica script, to be executed before MakeSPheno",action="store", default='',type=str) parser.add_argument("--SkipFlavorKit",help="Skip FlavorKit", action="store_true",default=False) parser.add_argument("--debug",help="Store extra debug info", action="store_true",default=False) parser.add_argument("--math", help="Mathematica executable name (to run on command line)",nargs="?",action="store", default='math') parser.add_argument('--local',help='Store data in the current directory/subdirectories only. Otherwise, will store data in the venv/user directory',action='store_true',default=False) try: args = parser.parse_args() except Exception as e: print(str(e)) parser.print_help() #print(info_message +str(e)) raise SystemExit model=args.ModelName modelname=str(args.ModelName).replace('/','-') mathexec = args.math runFlexibleSUSY=False if args.FlexibleSUSY != '': # FlexibleSUSY may have a different name to the SARAH one runFlexibleSUSY=True if args.All: args.MicrOMEGAs = True args.HiggsBounds = True args.HiggsSignals = True args.HiggsTools = True args.VevaciousPlusPlus = True args.BSMArt = True args.UFO = True args.flavio = True args.anyBSM = True #args.FlexibleSUSY = True runFlexibleSUSY=True args.SPheno = True if args.FlexibleSUSY == '': # FlexibleSUSY may have a different name to the SARAH one args.FlexibleSUSY = modelname ## Set up directories cwd = os.getcwd() scriptdir=os.path.join(cwd,'ScriptsAndLogs') if not os.path.exists(scriptdir): os.makedirs(scriptdir) ## Create the mathscript scriptname=os.path.join(scriptdir,'init'+modelname+'.m') """ Set up logging """ log = logging.getLogger(model) logfile=os.path.join(scriptdir,'prep_'+modelname+'.log') if os.path.exists(logfile): os.remove(logfile) if args.debug: log.setLevel(logging.DEBUG) else: log.setLevel(logging.INFO) ch = logging.StreamHandler() ch.setLevel(logging.INFO) fh = logging.FileHandler(logfile) if args.debug: fh.setLevel(logging.DEBUG) else: fh.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) ch.setFormatter(formatter) log.addHandler(fh) log.addHandler(ch) allcmdarguments=' '.join(sys.argv[1:]) log.info('Invoked with arguments: '+allcmdarguments) ## Load the paths ## Load the paths jsonFILE='HEPtoolpaths.json' if not os.path.exists(jsonFILE): try: from bsmart import get_heptools_path_file jsonFILE = get_heptools_path_file() except ImportError: pass if os.path.exists(jsonFILE): try: with open(jsonFILE) as json_data: paths_dict = json.load(json_data, object_pairs_hook=collections.OrderedDict) except Exception as e: #log.error('Failed to load json file '+file) log.error('Failed to load json file '+jsonFILE) log.error('Json exception given as ' +str(e)) raise SystemExit else: log.error('Failed to load json file '+jsonFILE) log.error('Please run BSMArt-InstallHEPTools first or ensure HEPtoolpaths.json is in the current directory or configuration folder.') raise SystemExit ## now check for paths if 'SARAH' not in paths_dict: log.error('Could not find SARAH path!') raise SystemExit if args.SPheno or args.All: if 'SPheno' not in paths_dict: log.warning('Could not find SPheno path!') args.SPheno = False if args.FlexibleSUSY != '': if 'FlexibleSUSY' not in paths_dict: log.warning('Could not find FlexibleSUSY path!') args.FlexibleSUSY= '' runFlexibleSUSY=False if args.MicrOMEGAs or args.All: if 'MicrOMEGAs' not in paths_dict: log.warning('Could not find MicrOMEGAs path!') args.MicrOMEGAs = False if args.HiggsBounds or args.All: if 'HiggsBounds' not in paths_dict: log.warning('Could not find HiggsBounds path!') args.HiggsBounds = False if args.HiggsSignals or args.All: if 'HiggsSignals' not in paths_dict: log.warning('Could not find HiggsSignals path!') args.HiggsSignals = False if args.HiggsTools or args.All: if 'HiggsBoundsRepo' not in paths_dict or 'HiggsSignalsRepo' not in paths_dict: log.warning('Could not find paths for HiggsTools repositories!') args.HiggsTools = False else: try: import Higgs except: args.HiggsTools = False if args.anyBSM: try: import anyBSM except: log.warning('Could not import anyBSM: disabling configuration') args.anyBSM = False if args.VevaciousPlusPlus or args.All: if 'Vevacious++' not in paths_dict: log.warning('Could not find Vevacious++ path!') args.VevaciousPlusPlus = False try: OF=open(scriptname,'w') except: log.error("Could not open script " + scriptname + " for writing") raise SystemExit OF.write('<<\"'+str(os.path.join(paths_dict["SARAH"],"SARAH.m"))+'\";\n') OF.write('Start[\"'+model+'\"];\n') #OF.write('DirectoryNamesFile=OpenWrite[ToFileName["'+scriptdir+'",modelname+"_DirectoryNames.txt"]];\n') #OF.write('WriteModelDirectories=True;') if len(args.Init) > 0: OF.write(args.Init+'\n') if args.SkipFlavorKit: OF.write('SkipFlavorKit=True;\n') #print(args.Options) if args.SPheno: OF.write('MakeSPheno['+args.Options+'];\n') # BSMArt path logic for AuxFiles # Updated to use importlib.resources for package compatibility aux_script_name = 'SARAH_BSMArt_extra.m' aux_script_path = None try: import importlib.resources from importlib.resources import files # Locate the resource # We need to extract it to a file accessible by Mathematica # We copy it to the script directory (where we are generating scripts) # Assuming bsmart.data.AuxFiles is a package/resource location # Check if we can find it ref = files('bsmart.data.AuxFiles').joinpath(aux_script_name) if ref.is_file(): dest_path = os.path.join(scriptdir, aux_script_name) # Read content and write to dest, or use as_file to copy? # Simple read/write is safest for text files with importlib.resources.as_file(ref) as f: shutil.copy(str(f), dest_path) aux_script_path = dest_path.replace('\\','/') # Ensure forward slashes for Mathematica if needed except Exception as e: log.warning('Could not extract ' + aux_script_name + ' from resources: ' + str(e)) # Fallback to local check or paths_dict if legacy if 'BSMArt' in paths_dict: p = os.path.join(paths_dict["BSMArt"],'AuxFiles',aux_script_name) if os.path.exists(p): aux_script_path = p if not aux_script_path: # Final fallback, assume it's in CWD or let SARAH fail if not found aux_script_path = aux_script_name # Check existence if not os.path.exists(aux_script_path) and not os.path.isfile(aux_script_path): log.error('Could not find auxiliary SARAH commands ('+aux_script_name+')!') raise SystemExit OF.write('<<\"'+aux_script_path+'\";\n') bsmartdatafile=os.path.join(scriptdir,'BSMArt_data_'+modelname+'.json') OF.write('fname=OpenWrite[\"'+bsmartdatafile+'\"];\n') OF.write('WriteString[fname,"{"];\n') OF.write('WriteBSMArtData[fname];\n') if (args.HiggsSignals or args.HiggsBounds): doHBHS=True OF.write('WriteHBData[fname];\n') elif args.HiggsTools: OF.write('WriteHBData[fname];\n') OF.write('WriteString[fname,"}"];\n') if args.SPheno: qnumbers_slha=os.path.join(scriptdir,'QNUMBERS_'+modelname+'.slha') OF.write('MakeQNUMBERSBSMArt["'+qnumbers_slha+'",If[Head[ExtraOddParticles] == List, ExtraOddParticles, {}]];\n') OF.write('Close[fname];\n') #if args.MicrOMEGAs or args.All: if args.MicrOMEGAs: OF.write('MakeCHep['+args.MOOptions+'];\n') #if args.VevaciousPlusPlus or args.All: if args.VevaciousPlusPlus: OF.write('MakeVevacious[Version->"++"];') if args.UFO: if args.anyBSM: log.info('Configure UFO to make all vertices') OF.write('MakeUFO[Exclude -> {}];') else: OF.write('MakeUFO[];') #OF.write('Close[DirectoryNamesFile];\n') OF.close() ### Now run the script log.info('Running SARAH') try: if args.debug: extra_debug_info=shell_command(mathexec + ' < '+scriptname,cwd,True) log.debug('SARAH output: '+extra_debug_info) else: _ = shell_command(mathexec+' < '+scriptname,cwd,True) except: log.critical('Error running Mathematica script') raise SystemExit try: with open(bsmartdatafile) as bsmart_json_data: bsmart_dict = json.load(bsmart_json_data, object_pairs_hook=collections.OrderedDict) except Exception as e: #log.error('Failed to load json file '+file) log.error('Failed to load json file '+bsmartdatafile) log.error('Json exception given as ' +str(e)) raise SystemExit ### Now start moving stuff around and compiling #sarahsphdir=os.path.join(paths_dict["SARAH"],'Output',modelname,'EWSB','SPheno') if runFlexibleSUSY: log.info('Configuring FlexibleSUSY for model') try: #with open(bsmartdatafile,'w') as bsmart_json_data: # json.dump(bsmart_dict, bsmart_json_data, indent=4) if 'MathematicaUserBaseDirectory' in paths_dict: os.environ["MATHEMATICA_USERBASE"] = paths_dict['MathematicaUserBaseDirectory'] os.environ["WOLFRAM_USERBASE"] = paths_dict['MathematicaUserBaseDirectory'] #os.environ["MATHEMATICA_USERBASE"] = paths_dict['FlexibleSUSY'] os.chdir(paths_dict['FlexibleSUSY']) #_ = shell_command('math < '+scriptname,cwd,True) _ = shell_command('./createmodel --name=%s -f' % args.FlexibleSUSY,paths_dict['FlexibleSUSY'],True,True) # errors fatal, log output #os.system('./createmodel --name=%s -f' % args.FlexibleSUSY) try: looptoollib=get_lib_path(paths_dict['LoopTools']) except Exception as e: log.critical('Failed to get python library dir for LoopTools. '+str(e)) try: collierlib=get_lib_path(paths_dict['COLLIER']) except Exception as e: log.critical('Failed to get python library dir for Collier. ' +str(e)) inclist=' --with-loop-libraries=collier,looptools --with-looptools-libdir='+looptoollib+' --with-looptools-incdir='+os.path.join(paths_dict['LoopTools'],'include') inclist+=' --with-collier-libdir='+collierlib+' --with-collier-incdir='+os.path.join(paths_dict['COLLIER'],'include') cmd='./configure --with-math-cmd="%s" --with-models=%s' %(mathexec,args.FlexibleSUSY) cmd+= inclist #_ = shell_command(cmd,paths_dict['FlexibleSUSY'],True) #os.system(cmd) _ = shell_command(cmd,paths_dict['FlexibleSUSY'],True,True) try: import multiprocessing as mp cpus=mp.cpu_count() makeline='make -j%d' %cpus except: makeline='make' #os.system(makeline) _ = shell_command(makeline,paths_dict['FlexibleSUSY'],True,True) bsmart_dict['FSmodelname']=args.FlexibleSUSY os.chdir(cwd) except Exception as e: log.error('Failed to build FlexibleSUSY model: '+str(e)) internalmodelname=str(bsmart_dict['BSMArtmodelname']) if args.SPheno: sarahsphdir=bsmart_dict["SPhenoOutputDir"] sphenotarget=os.path.join(paths_dict["SPheno"],modelname) if not os.path.exists(sarahsphdir): log.error("SPheno output not generated!") raise SystemExit if os.path.exists(sphenotarget): ## remove shutil.rmtree(sphenotarget) shutil.copytree(sarahsphdir,sphenotarget) log.info('Building SPheno') try: _ = shell_command("make Model="+modelname,paths_dict["SPheno"],True) except: log.critical('Error compiling SPheno script') raise SystemExit else: # remove the SPhenoOutputDir from the json data file, that way we know SPheno wasn't built if 'SPhenoOutputDir' in bsmart_dict: bsmart_dict.pop('SPhenoOutputDir') #if args.MicrOMEGAs or args.All: if args.MicrOMEGAs: log.info('Building MicrOMEGAs') sarahmodir=os.path.join(paths_dict["SARAH"],'Output',modelname,'EWSB','CHep') targetmodir=os.path.join(paths_dict["MicrOMEGAs"],'SARAH_'+modelname) if os.path.exists(targetmodir): shutil.rmtree(targetmodir) try: _ = shell_command('./newProject SARAH_'+modelname,paths_dict["MicrOMEGAs"],True) except: log.error('Failed to create new MicrOMEGAs project') raise SystemExit #for ff in ['prtcls1.mdl','func1.mdl','extlib1.mdl','vars1.mdl','lgrng1.mdl']: for ff in ['prtcls1.mdl','func1.mdl','vars1.mdl','lgrng1.mdl']: shutil.copy(os.path.join(sarahmodir,ff),os.path.join(targetmodir,'work','models')) MOver=os.path.basename(paths_dict["MicrOMEGAs"]) if len(MOver) < 11 or MOver[10] !='_': ## wtf? Assume latest version MOver='6.0.0' else: MOver=MOver[11:] if MOver[1] !='.': MOver='6.0.0' MOver=float(MOver[0]+MOver[2:]) if args.BSMArt or args.All: # Determined executale/cpp name if MOver<52: moexec='MicrOmegas_v5.0_BSMArt' elif MOver<53.41: moexec='MicrOmegas_v5.2_BSMArt' elif MOver<60: moexec='MicrOmegas_v5.3_BSMArt_xsections' else: moexec='MicrOmegas_v6.1_BSMArt_xsections' mocpp=moexec+'.cpp' # Logic to find mocpp file using importlib.resources src_mocpp = None try: import importlib.resources from importlib.resources import files ref = files('bsmart.data.AuxFiles').joinpath(mocpp) if ref.is_file(): with importlib.resources.as_file(ref) as f: shutil.copy(str(f),targetmodir) # It's now in targetmodir as mocpp (shutil.copy copies filename) src_mocpp = os.path.join(targetmodir, mocpp) except Exception as e: log.warning('Could not find ' + mocpp + ' in resources: ' + str(e)) # Fallback if 'BSMArt' in paths_dict: p = os.path.join(paths_dict["BSMArt"],'AuxFiles',mocpp) if os.path.exists(p): shutil.copy(p,targetmodir) src_mocpp = os.path.join(targetmodir, mocpp) if not src_mocpp or not os.path.exists(src_mocpp): log.error('Could not find BSMArt micrOMEGAs driver: '+mocpp) raise SystemExit command='make main='+mocpp MOexec=os.path.join(targetmodir,moexec) else: MOexec=os.path.join(targetmodir,'CalcOmega_with_DDetection_MOv5') command='make main=CalcOmega_with_DDetection_MOv5.cpp' shutil.copy(os.path.join(sarahmodir,'CalcOmega_with_DDetection_MOv5.cpp'),targetmodir) shutil.copy(os.path.join(sarahmodir,'CalcOmega_MOv5.cpp'),targetmodir) try: _ = shell_command(command,targetmodir,False) except: log.error('Failed to compile MicrOMEGAs project') raise SystemExit if not os.path.exists(MOexec): log.error('Error compiling MicrOMEGAs code (no executable found)') raise SystemExit #if MOver > 53.4 and args.BSMArt: # MOexec=MOexec+' --xsections' bsmart_dict['MicrOMEGAs executable'] = MOexec bsmart_dict['MicrOMEGAs version'] = MOver # This was at the end of micromegas; now I will just do it automatically because of updates e.g. in FlexibleSUSY with open(bsmartdatafile, 'w') as fp: json.dump(bsmart_dict, fp, indent=4) ### Now set up BSMArt scan directory, if required if not args.local: from bsmart import get_bsmart_config_dir global_config_dir = get_bsmart_config_dir() if os.path.isdir(global_config_dir): global_bsmartdatafile=os.path.join(global_config_dir,'BSMArt_data_'+modelname+'.json') with open(global_bsmartdatafile, 'w') as fp: json.dump(bsmart_dict, fp, indent=4) #if not args.BSMArt and not args.All: if not args.BSMArt: sys.exit() log.info("Setting up BSMArt") includecodes={ 'SPheno':args.SPheno, 'FlexibleSUSY': runFlexibleSUSY, 'MicrOMEGAs':args.MicrOMEGAs, 'Vevacious++':args.VevaciousPlusPlus, 'HiggsSignals':args.HiggsSignals, 'HiggsBounds':args.HiggsBounds, 'HiggsTools':args.HiggsTools, 'SModelS': True, 'Flavio': True} prepareBSMArt.makeBSMArtTemplates(modelname,bsmart_dict,paths_dict,includecodes)
if __name__ == "__main__": main()