#!/usr/bin/env python3
"""
.. module:: runtime
:synopsis: Tools to gather info about the runtime environment,
( nCPUs() ), or obtain file type ( filetype() ). Pointer
to model file is also kept here.
.. moduleauthor:: Wolfgang Waltenberger <wolfgang.waltenberger@gmail.com>
"""
from typing import Union, Text, Dict
import os
## place to keep the pointer to the model file (default = mssm)
modelFile="smodels.share.models.mssm"
_experimental = { "truncatedgaussians": False,
"spey": False } ## experimental features
_deltas_rel_default = .2 ## the default relative error on the signal strength
[docs]def checkForIncompatibleModuleVersions() -> bool:
""" for 3.1.0, at release time, we have the problem that scipy 1.16
does not work with pyhf 0.7.6. we warn about such situations.
:returns: false if incompatible version found, else true
"""
from importlib.metadata import version
if version("scipy")[:6] in ["1.16.0", "1.16.1", "1.16.2"]:
print ( f"SModelS warning: scipy v{version('scipy')} is installed, but there is " \
"a known optimizer issue in this scipy version that causes errors " \
"for some pyhf models. " \
"c.f. https://github.com/scikit-hep/pyhf/issues/2593. You have been warned." )
return False
return True
checkForIncompatibleModuleVersions()
[docs]def returnGitCommitId():
""" if we are in a git-managed repository, return the git commit id """
try:
import subprocess
# Check if we're inside a Git repository
subprocess.run(
['git', 'rev-parse', '--is-inside-work-tree'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True
)
# Get the current commit hash
commit = subprocess.check_output(
['git', 'rev-parse', 'HEAD'],
stderr=subprocess.DEVNULL
).decode('utf-8').strip()
return commit
except subprocess.CalledProcessError:
# Not a Git repo, or git command failed—do nothing
return None
[docs]def printEnvironmentInfo( args : Dict ):
""" very simple method that prints out info relevant to debugging
machine-dependent problems
:param args: a dictionary with options, currently only a "colors" option
is available
:returns: true if all depenendencies are met
"""
from smodels.base.smodelsLogging import colors
colors.on = True if "colors" in args and args["colors"] == True else False
import importlib, platform
modules = [ "scipy", "sympy", "numpy",
"pyslha", "unum", "pyhf" ]
print("Environment Information:")
print(f"Operating System: {colors.green}{platform.system()} {platform.release()}{colors.reset}")
print(f"Python Version: {colors.green}{platform.python_version()}{colors.reset}")
print(f"Machine Architecture: {colors.green}{platform.machine()}{colors.reset}")
print(f"Processor: {colors.green}{platform.processor()}{colors.reset}")
print("\nModule Versions:")
depsMet = True
for module_name in modules:
try:
module = importlib.import_module(module_name)
version = getattr(module, '__version__', 'Unknown version attribute')
print(f"{module_name:<12}: {colors.green}{version}{colors.reset}")
except ImportError:
print(f"{module_name:<12}: Not installed")
depsMet = False
gitId = returnGitCommitId()
if gitId != None:
print ( f"\ngit commit: {gitId}" )
return depsMet
[docs]def filetype ( filename : os.PathLike ) -> Union[Text,None]:
""" obtain information about the filetype of an input file,
currently only used to discriminate between slha and lhe
files.
:returns: filetype as string("slha" or "lhe"),
None if file does not exist, or filetype is unknown.
"""
import os
if not os.path.exists ( filename ):
return None
if filename.endswith(".slha"):
return "slha"
if filename.endswith(".SLHA"):
return "slha"
if filename.endswith(".lhe" ):
return "lhe"
if filename.endswith(".LHE" ):
return "lhe"
try:
with open ( filename, "rt" ) as f:
for line in f:
if "<LesHouchesEvents" in line:
return "lhe"
if "<event>" in line:
return "lhe"
if "block " in line.lower():
return "slha"
except UnicodeDecodeError:
## a binary file??
return None
return None
[docs]def experimentalFeature( feature : str ) -> Union[None,bool]:
""" method to check if a certain experimental feature is enabled.
can be turned on and off via options:experimental in parameters.ini.
:param feature: ask for feature
:returns: None if feature does not exist, else boolean
"""
if not feature in _experimental:
return None
return _experimental[feature]
[docs]def nCPUs():
""" obtain the number of *available* CPU cores on the machine, for several
platforms and python versions. """
try:
# next few lines taken from
# https://stackoverflow.comhttps//stackoverflow.com/questions/1006289/how-to-find-out-the-number-of-cpus-using-python/questions/1006289/how-to-find-out-the-number-of-cpus-using-python
import re
with open('/proc/self/status') as f:
m = re.search(r'(?m)^Cpus_allowed:\s*(.*)$', f.read())
if m:
res = bin(int(m.group(1).replace(',', ''), 16)).count('1')
if res > 0:
return res
except IOError:
pass
try:
import multiprocessing
return multiprocessing.cpu_count()
except ImportError:
pass
try:
import psutil
return psutil.NUM_CPUS
except ImportError:
pass
try:
import os
res = int(os.sysconf('SC_NPROCESSORS_ONLN'))
if res>0: return res
except ImportError:
pass
return None
if __name__ == "__main__":
printEnvironmentInfo()
# print ( f"This machine has {nCPUs()} CPUs" )