"""
.. module:: experiment.smsResults
:synopsis: Centralized facility to access the SMS results.
.. moduleauthor:: Wolfgang Waltenberger <wolfgang.waltenberger@gmail.com>
.. moduleauthor:: Ursula Laa <Ursula.Laa@assoc.oeaw.ac.at>
.. moduleauthor:: Doris Proschofsky <Doris.Proschofsky@assoc.oeaw.ac.at>
.. moduleauthor:: Wolfgang Magerl <wolfgang.magerl@gmail.com>
"""
from smodels.tools.physicsUnits import TeV, pb, fb, GeV
## from smodels.tools import rcFile
from smodels.experiment import smsHelpers
from smodels.experiment.experimentExceptions import MetaInfoError
from smodels.experiment.smsHelpers import getPath
from smodels.tools.uniqueLogFilter import UniqueFilter
import logging
logger = logging.getLogger(__name__)
logger.addFilter(UniqueFilter())
allresults = {}
constraints = {}
conditions = {}
[docs]def getAllResults(sqrts=None, experiment=None):
"""
Get all analyses and topologies that have results.
"""
import os
key = str(sqrts)+str(experiment)
if key in allresults:
return allresults[key]
sqrtsList = smsHelpers.sqrts
experimentList = smsHelpers.experiments
if sqrts:
sqrtsList = [sqrts]
if experiment:
experimentList = [experiment]
ret = {}
for s in sqrtsList:
for e in experimentList:
if not os.path.exists("%s/%s/%s/" % (smsHelpers.base, s, e)):
logger.warning("Expected directory %s/%s was not found in the database at %s" % (s, e, smsHelpers.base))
continue
dirs = os.listdir("%s/%s/%s" % (smsHelpers.base, s, e))
for ana in dirs:
if os.path.exists("%s/%s/%s/%s/info.txt" % (smsHelpers.base, s, e, ana)):
topos = getTopologies(ana)
ret[ana] = topos
allresults[key] = ret
return ret
[docs]def getTopologies(analysis):
"""
Get all topologies of an analysis with constraints.
"""
path = smsHelpers.getPath(analysis)
x = getConstraints(analysis, path=path)
return x.keys()
[docs]def getConstraints(analysis, topology="all", path=None):
"""
Get constraints of an analysis.
:returns: dictionary of constraints, if topology == "all"; single
constraint for the passed topology, if only one topology is passed;
None if non-existent;
"""
key = analysis + topology + str(path)
if key in constraints:
return constraints[key]
path = smsHelpers.getPath(analysis, path)
ret = smsHelpers.getLines(analysis, path, "constraint")
if topology == "all":
constraints[key] = ret
return ret
if not topology in ret:
constraints[key] = None
return None
constraints[key] = ret[topology]
return ret[topology]
[docs]def getBranchCondition(anaName, txName, path=None):
"""
Get the branch condition for an analysis.
:returns: string containing the branch condition (e.g. equal branch masses)
"""
path = smsHelpers.getPath(anaName, path)
ret = smsHelpers.getLines(anaName, path, "branchcondition")
if not txName in ret: return None
else: return ret[txName]
[docs]def getSqrts(analysis, path=None):
""" get the center-of-mass energy of the analysis.
"""
sqrts = smsHelpers.getMetaInfoField(analysis, "sqrts", path)
try:
return float(sqrts) * TeV
except ValueError:
try:
return eval(sqrts)
except:
pass
return sqrts
[docs]def getConditions(analysis, topology="all", fuzzy=True, path=None):
"""
Get conditions of an analysis.
:returns: dictionary of conditions, if topology == "all"; single condition
for the passed topology, if only one topology is passed; None if
non-existent.
"""
key = analysis + topology + str(fuzzy) + str(path)
if key in conditions:
return conditions[key]
path = smsHelpers.getPath(analysis, path)
if fuzzy:
ret = smsHelpers.getLines(analysis, path, "fuzzycondition")
else:
ret = smsHelpers.getLines(analysis, path, "condition")
if topology == "all":
conditions[key] = ret
return ret
if not topology in ret:
conditions[key] = None
return None
conditions[key] = ret[topology]
return ret[topology]
[docs]def getaxes(analysis, topology=None, path=None):
"""Get information about the histogram axes for an analysis.
For each topology list of dictionary, each dictionary corresponds to one
histogram. The key axes gives string (mx-my), the key mz gives information
on other masses, if you supply a topology, returns list for this topology
only.
"""
if not _exists(analysis, topology=None):
return None
try:
st = smsHelpers.getMetaInfoField(analysis, "axes", path)
except MetaInfoError:
logger.error("Meta info field 'axes' does not exist in %s.", analysis)
st = None
if not st:
if not topology:
# Cannot return default without info on topology
return None
# If there is no information about the axes, return the default
return [{'axes': 'M1-M0', 'mz': None}]
st = st.split(',')
d = {}
for i in range(len(st)):
l = st[i].split(':')
nm = l[0].replace(" ", "")
d[nm] = []
m = l[1].split('-')
for j in range(len(m)):
n = m[j].split()
if len(n) == 2:
d[nm].append({'axes': n[0] + '-' + n[1], 'mz': None})
else:
d[nm].append({'axes': n.pop(0) + '-' + n.pop(0), 'mz': n})
if topology:
topology = topology.replace(" ", "")
if not d or not topology in d:
# Return None, if topology does not exist
return None
else:
return d[topology]
return d
[docs]def setBase(base):
"""
Set the base directory of the database.
"""
smsHelpers.base = base
[docs]def getBase():
"""
Return the base directory of the database.
"""
return smsHelpers.base
[docs]def getURL(analysis, path=None):
"""
Get the URL of an analysis.
"""
logger.warning ("getURL is deprecated")
return smsHelpers.getMetaInfoField(analysis, "url", path)
[docs]def hasURL(analysis, path=None):
"""
Check if URL of an analysis exists."""
logger.warning("hasURL is deprecated")
return smsHelpers.getMetaInfoField(analysis, "url", path)
[docs]def getPAS(analysis, path=None):
"""
Get the PAS of an analysis.
"""
logger.warning("getPAS is deprecated")
return smsHelpers.getMetaInfoField(analysis, "pas", path)
[docs]def getJournal(analysis, path=None):
"""
Get the journal of an analysis.
"""
logger.warning ("getJournal is deprecated")
return smsHelpers.getMetaInfoField(analysis, "journal", path)
[docs]def getLumi(analysis, path=None):
"""
Get the integrated luminosity for an analysis.
"""
lumifb = smsHelpers.getMetaInfoField(analysis, "lumi", path)
try:
return float(lumifb) / fb
except ValueError:
try:
return eval(lumifb)
except:
pass
return lumifb
[docs]def isPrivate(analysis, path=None):
"""
Check if analysis is flagged as private.
"""
field=smsHelpers.getMetaInfoField(analysis, "private", path)
if field==None: return False
return bool(int(field))
[docs]def isSuperseded (analysis, path=None):
"""
check if analysis is superseded, if yes,
return analysis name of newer analysis
"""
return smsHelpers.getMetaInfoField (analysis, "superseded_by", path)
[docs]def getExperiment(analysis, path=None):
"""
Check if path is to ATLAS directory, else return CMS.
"""
logger.warning("getExperiment is deprecated")
path1 = getPath(analysis, path)
if path1.find("ATLAS") > -1:
return "ATLAS"
return "CMS"
[docs]def considerExperiment(experiment):
"""
Define the experiment to be considered.
"""
smsHelpers.experiments = [experiment]
[docs]def considerSqrts(sqrts):
"""
Define the center of mass energies to be considered (as strings in list format)
"""
smsHelpers.sqrts = [sqrts]
def _exists(analysis, topology, path=None):
"""
Check if the dictionary 'limit_topo' in <path>/sms.py exists.
For topologies with intermediate masses, check if all dictionaries listed
in the axes-information exist. If topology == None, check if
<path>/sms.py exists.
"""
path2 = smsHelpers.getPath(analysis, path)
if not topology:
import os
base = smsHelpers.base
pydict = "%s/%s/%s/sms.py" % (base, path2, analysis)
if os.path.exists(pydict):
return True
else:
return False
axes = getaxes(analysis, topology)
if not axes:
return False
hasDict = smsHelpers.hasDictionary(analysis, path2)
if not hasDict:
return False
return smsHelpers.hasDictionary(analysis, path2, topology)
[docs]def getUpperLimit(analysis, topology, mx=None, my=None, path=None,
interpolate=False, expected=False):
"""
Get the upper limit for path/analysis/topology.
:returns: None, if it does not exist; entire dictionary, if mx and my are
None; upper limit at mx/my, if mx and my are floats;
"""
path = smsHelpers.getPath(analysis, path)
if smsHelpers.hasDictionary(analysis, path):
return getUpperLimitFromDictionary(analysis, topology, mx, my, path,
interpolate=interpolate,
expected=expected)
logger.warning("No upper limits found for %s", analysis)
return None
[docs]def getUpperLimitFromDictionary(analysis, topology, mx=None, my=None,
path=None, png=None, interpolate=False,
expected=False):
"""
Get an upper limit from the python dictionary.
"""
dictionary = smsHelpers.getUpperLimitDictionary(analysis, topology, path,
expected=expected)
if dictionary == None:
return dictionary
if type(mx) == type(None) and type(my) == type(None):
return dictionary
if type(mx) == type(None) or type(my) == type(None):
logger.error("Requesting upper limits for mx = %s and my = %s", mx, my)
return None
if not (type(mx) == type(1.) or type(mx) == type(GeV)):
return dictionary
if not getInterpolatedUpperLimitDelaunay(dictionary, mx, my): return None
return getInterpolatedUpperLimitDelaunay(dictionary, mx, my) * pb
[docs]def getInterpolatedUpperLimitDelaunay(dictionary, inmx, inmy):
"""
Get interpolated upper limit from dictionary at point (inmx, inmy).
:param dictionary: dictionary (sms.py), contains upper limits of one
analysis and one topology
:param inmx: mass point on x-axis
:param inmy: mass point on y-axis
:returns: interpolated upper limit at point (inmx, inmy)
"""
import numpy as np
import scipy.interpolate as ip
try:
mx = inmx / GeV
my = inmy / GeV
if not inConvexHull(dictionary, mx, my):
logger.debug("Cannot interpolate for (%s, %s), point is not in "
"convex hull.", str(inmx), str(inmy))
return None
n = 0
for k in dictionary:
n += len(dictionary[k])
points = np.zeros((n, 2))
values = np.zeros((n))
i = 0
for x in dictionary:
for y in dictionary[x]:
points[i] = [x, y]
values[i] = dictionary[x][y]
i += 1
gridX = np.zeros((1, 1))
gridY = np.zeros((1, 1))
gridX = mx
gridY = my
return float(ip.griddata(points, values, (gridX, gridY),
method='linear'))
except Exception as e:
logger.error("Cannot interpolate %s. Using closest value instead.", e)
if not inConvexHull(dictionary, inmx, inmy):
return False
return getClosestValue(dictionary, inmx, inmy)
[docs]def inConvexHull(dictionary, mx, my):
"""
Check if (mx,my) point is in the data dictionary.
"""
import numpy
from scipy.spatial import Delaunay
pointlist = []
for k in dictionary.keys():
for ki in dictionary[k].keys():
pointlist.append([k, ki])
p = numpy.array(pointlist)
dela = Delaunay(p)
return dela.find_simplex((mx, my)) >= 0
[docs]def getClosestValue(dictionary, mx, my):
"""
Get the upper limit of the point in dictionary that is closest to mx and
my, assuming that dictionary is a dictionary of mx, my, ul.
"""
closest = 9999999
retul = None
for (dmx, dmv) in dictionary.items():
for (dmy, ul) in dmv.items():
dist = (mx - dmx) ** 2 + (my - dmy) ** 2
if dist < closest:
closest = dist
retul = ul
if closest > 20.**2:
# Return False, if distance > 20 GeV from closest point
return False
return retul