"""
The :py:mod:`bamboo.root` module collects a set of thin wrappers around ROOT
methods, and centralizes the import of the Cling interpreter global namespace
in PyROOT. For compatibility, it is recommended that user code uses
``from bamboo.root import gbl`` rather than ``import ROOT as gbl`` or
``from cppyy import gbl``.
"""
import functools
from contextlib import contextmanager
import pkg_resources
import ROOT; gbl = ROOT # noqa: E702
_rootVersion = gbl.gROOT.GetVersion()
_rootVersion_split = tuple(int(iv) for tk in _rootVersion.split("/") for iv in tk.split("."))
if _rootVersion_split[0] == 6 and _rootVersion_split[1] < 22:
gbl.PyConfig.IgnoreCommandLineOptions = True
_includePaths = set()
_headers = set()
_dynamicPaths = set()
_libraries = set()
[docs]
def addIncludePath(incPath):
""" Add an include path to the ROOT interpreter """
global _includePaths
_includePaths.add(incPath)
gbl.gInterpreter.AddIncludePath(incPath)
[docs]
def addDynamicPath(libPath):
""" Add a dynamic library path to the ROOT interpreter"""
global _dynamicPaths
_dynamicPaths.add(libPath)
gbl.gSystem.AddDynamicPath(libPath)
[docs]
def loadLibrary(libName):
""" Load a shared library in the ROOT interpreter """
global _libraries
_libraries.add(libName)
st = gbl.gSystem.Load(libName)
if st == -1:
raise RuntimeError(f"Library {libName} could not be found")
elif st == -2:
raise RuntimeError(f"Version match for library {libName}")
[docs]
def findLibrary(libName):
""" Check if a library can be found, and returns the path in that case """
ret = gbl.gSystem.FindDynamicLibrary(gbl.TString(libName), True)
if ret != "":
return str(ret)
[docs]
def loadDependency(bambooLib=None, includePath=None, headers=None, dynamicPath=None, libraries=None):
"""
Load a C++ extension
:param bambooLib: name(s) of the bamboo extension libraries, if any
:param includePath: include directory for headers
:param headers: headers to load explicitly (which can depend on other headers in the inclue path)
:param dynamicPath: dynamic library path to add
:param libraries: additional shared libraries to load
"""
def asList(arg):
if isinstance(arg, str):
return [arg]
else:
return arg
if dynamicPath:
for pth in asList(dynamicPath):
addDynamicPath(pth)
if libraries:
for lib in asList(libraries):
loadLibrary(f"lib{lib}")
if includePath:
for pth in asList(includePath):
addIncludePath(pth)
if headers:
for hdr in asList(headers):
loadHeader(hdr)
if bambooLib:
for lib in asList(bambooLib):
loadLibrary(f"lib{lib}")
def getIMTPoolSize():
return ROOT.GetThreadPoolSize() if ROOT.IsImplicitMTEnabled() else 0
@contextmanager
def returnToDirectory():
gpwd = gbl.gDirectory.GetPath()
yield
gbl.gDirectory.cd(gpwd)
[docs]
class once:
""" Function decorator to make sure things are not loaded more than once """
def __init__(self, fun):
self.fun = fun
self.has_run = False
functools.update_wrapper(self, fun)
def __call__(self, *args, **kwargs):
if not self.has_run:
self.has_run = True
return self.fun(*args, **kwargs)
_defaultHeaders = ("Math/VectorUtil.h", "bamboohelpers.h", "range.h",
"LumiMask.h", "bamboorandom.h",
"scalefactors.h", "BTagCalibrationStandalone.h")
_defaultBambooLibs = ("BambooLumiMask", "BambooRandom", "BinnedValues")
_defaultDependencies = ("ROOT::ROOTDataFrame", "ROOT::GenVector")
@once
def loadBambooExtensions():
# Add extension libraries and necessary header files to the ROOT interpreter
incDir = pkg_resources.resource_filename("bamboo", "include")
libDir = pkg_resources.resource_filename("bamboo", "lib")
import os.path
if not (os.path.exists(incDir) and os.path.exists(libDir)):
raise RuntimeError(
f"Could not find {incDir} and/or {libDir}, this may happen when running "
"from the source repository and using a virtualenv with non-editable install")
addIncludePath(incDir)
addDynamicPath(libDir)
# now load default headers and libraries
for lib in _defaultBambooLibs:
loadLibrary(f"lib{lib}")
for fname in _defaultHeaders:
loadHeader(fname)
@once
def loadJMESystematicsCalculators(backend=None):
try:
import CMSJMECalculators # noqa: F401
except ImportError:
raise RuntimeError(
"Could not load jet&MET correction and systematic variations calculators. "
"Please install them (see https://gitlab.cern.ch/cp3-cms/CMSJMECalculators "
"for more information)"
) from None
loadBambooExtensions()
loader = backend.addDependency if backend else loadDependency
import pkg_resources
inc_dir = pkg_resources.resource_filename("CMSJMECalculators", "include")
libDir = pkg_resources.resource_filename("CMSJMECalculators", "lib")
loader(includePath=inc_dir, headers="JMESystematicsCalculators.h")
loader(dynamicPath=libDir, libraries="CMSJMECalculators")
if backend:
calc_dir = pkg_resources.resource_filename("CMSJMECalculators", "cmake")
backend.addDependency(
package="CMSJMECalculators", cmakeArgs=f"-DCMSJMECalculators_DIR={calc_dir}",
)
@once
def loadRochesterCorrectionCalculator(backend=None):
loadBambooExtensions()
loader = backend.addDependency if backend else loadDependency
loader(bambooLib="RoccoR", headers="RochesterCorrectionCalculator.h")
getattr(gbl, "RochesterCorrectionCalculator::result_t") # trigger dictionary generation
@once
def loadTauESCorrectionCalculator(backend=None):
loadBambooExtensions()
loader = backend.addDependency if backend else loadDependency
loader(headers="TauESCorrectionCalculator.h")
getattr(gbl, "TauESCorrectionCalculator::result_t") # trigger dictionary generation
@once
def loadSVfitCalculator(backend=None, pathToSVfit=""):
loadBambooExtensions()
loader = backend.addDependency if backend else loadDependency
import os
incPath, dynPath = None, None
if "VIRTUAL_ENV" in os.environ and pathToSVfit == "":
incPath = os.path.join(os.environ["VIRTUAL_ENV"], "../ClassicSVfit/interface")
dynPath = os.path.join(os.environ["VIRTUAL_ENV"], "../build-ClassicSVfit-FastMTT/src")
elif pathToSVfit != "":
incPath = os.path.join(pathToSVfit, "../ClassicSVfit/interface")
dynPath = os.path.join(pathToSVfit, "../build-ClassicSVfit-FastMTT/src")
else:
raise RuntimeError(
"Could not load SVfit."
)
loader(headers="SVfitBambooProducer.h",
includePath=incPath, dynamicPath=dynPath,
libraries="ClassicSVfit")
@once
def loadLibTorch(backend=None):
if loadTensorflowC.has_run:
raise RuntimeError("If loading both Tensorflow-C and libtorch, the latter should be loaded first")
loadBambooExtensions()
torchLib = findLibrary("libtorch")
dynPath = None
if (not torchLib) and pkg_resources.resource_filename("torch", "lib"):
dynPath = pkg_resources.resource_filename("torch", "lib")
loader = backend.addDependency if backend else loadDependency
loader(bambooLib="BambooTorch", headers="bambootorch.h", dynamicPath=dynPath)
def _addVirtualEnvPaths():
import os
incPath, dynPath = None, None
if "VIRTUAL_ENV" in os.environ:
incPath = addIncludePath(os.path.join(os.environ["VIRTUAL_ENV"], "include"))
dynPath = addDynamicPath(os.path.join(os.environ["VIRTUAL_ENV"], "lib"))
return incPath, dynPath
@once
def loadTensorflowC(backend=None):
loadBambooExtensions()
tfLib = findLibrary("libtensorflow")
incPath, dynPath = None, None
if not tfLib:
incPath, dynPath = _addVirtualEnvPaths()
loader = backend.addDependency if backend else loadDependency
loader(bambooLib="BambooTensorflowC", headers="bambootensorflowc.h",
includePath=incPath, dynamicPath=dynPath, libraries="tensorflow")
@once
def loadlwtnn(backend=None):
loadBambooExtensions()
lwtnnLib = findLibrary("liblwtnn")
incPath, dynPath = None, None
if not lwtnnLib:
incPath, dynPath = _addVirtualEnvPaths()
loader = backend.addDependency if backend else loadDependency
loader(bambooLib="BambooLwtnn", headers="bamboolwtnn.h",
includePath=incPath, dynamicPath=dynPath, libraries="lwtnn")
@once
def loadONNXRuntime(backend=None):
loadBambooExtensions()
onnxRuntimeLib = findLibrary("libonnxruntime")
incPath, dynPath = None, None
if not onnxRuntimeLib:
incPath, dynPath = _addVirtualEnvPaths()
onnxRuntimeLib = findLibrary("libonnxruntime")
import os.path
incDir = os.path.join(os.path.dirname(os.path.dirname(onnxRuntimeLib)), "include")
if os.path.isdir(os.path.join(incDir, "onnxruntime")):
incDir = os.path.join(incDir, "onnxruntime")
onnx_cxx_hdr = os.path.join(incDir, "core", "session", "onnxruntime_cxx_api.h")
if not os.path.isfile(onnx_cxx_hdr):
raise RuntimeError(f"Could not find onnxruntime header {onnx_cxx_hdr}")
loader = backend.addDependency if backend else loadDependency
loader(bambooLib="BambooONNXRuntime", headers="bambooonnxruntime.h",
includePath=os.path.dirname(onnx_cxx_hdr),
dynamicPath=dynPath, libraries="onnxruntime")
@once
def loadBambooSOFIE(backend=None):
loadBambooExtensions()
loader = backend.addDependency if backend else loadDependency
loader(headers="bamboosofie.h")
@once
def loadcorrectionlib(backend=None):
loader = backend.addDependency if backend else loadDependency
loader(headers="correction.h",
includePath=pkg_resources.resource_filename("correctionlib", "include"),
dynamicPath=pkg_resources.resource_filename("correctionlib", "lib"), libraries="correctionlib"
)