Commit c11cb966 authored by Marcel Rieger's avatar Marcel Rieger
Browse files

Catch cases where limit plots cannot be done with plot opt 4 and fallback to 3.

parent fb8d8e92
......@@ -15,7 +15,7 @@ from dhi.util import (
)
from dhi.plots.likelihoods import evaluate_likelihood_scan_1d, evaluate_likelihood_scan_2d
from dhi.plots.util import (
use_style, draw_model_parameters, get_graph_points, get_contours, get_text_extent,
use_style, draw_model_parameters, invert_graph, get_graph_points, get_contours, get_text_extent,
)
......@@ -606,68 +606,6 @@ def get_auto_contour_levels(values, steps=(1,)):
return levels
def repeat_graph(g, n):
points = sum((n * [p] for p in zip(*get_graph_points(g))), [])
g_repeated = g.__class__(len(points))
for i, (x, y) in enumerate(points):
g_repeated.SetPoint(i, x, y)
return g_repeated
def invert_graph(g, x_min=None, x_max=None, y_min=None, y_max=None, x_axis=None, y_axis=None,
offset=0.):
# get all graph values
x_values, y_values = get_graph_points(g)
# get default frame values
if x_min is None:
if x_axis:
x_min = x_axis.GetXmin() - 0.01 * (x_axis.GetXmax() - x_axis.GetXmin())
else:
x_min = min(x_values)
if x_max is None:
if x_axis:
x_max = x_axis.GetXmax() + 0.01 * (x_axis.GetXmax() - x_axis.GetXmin())
else:
x_max = max(x_values)
if y_min is None:
if y_axis:
y_min = y_axis.GetXmin() - 0.01 * (y_axis.GetXmax() - y_axis.GetXmin())
else:
y_min = min(y_values)
if y_max is None:
if y_axis:
y_max = y_axis.GetXmax() + 0.01 * (y_axis.GetXmax() - y_axis.GetXmin())
else:
y_max = max(y_values)
# define outer "frame" points
bl = (x_min - offset, y_min - offset)
tl = (x_min - offset, y_max + offset)
tr = (x_max + offset, y_max + offset)
br = (x_max + offset, y_min - offset)
corners = [tr, br, bl, tl]
# find the corner that is closest to the graph start point
dist = lambda x, y: ((x - x_values[0])**2. + (y - y_values[0])**2.)**0.5
start_index = min(list(range(4)), key=lambda i: dist(corners[i][0], corners[i][1]))
# to invert the graph, create a new graph whose points start with an outer frame consisting of
# the 4 corners, the graph points itself, the first point of the graph again to close it, and
# ending with the closest corner again
points = (2 * corners)[start_index:start_index + 5]
points.extend(zip(x_values, y_values))
points.append((x_values[0], y_values[0]))
points.append(corners[start_index])
# copy the graph and fill points
g_inv = g.__class__(len(points))
for i, (x, y) in enumerate(points):
g_inv.SetPoint(i, x, y)
return g_inv
def locate_xsec_labels(graphs, level, label_width, pad_width, pad_height, x_min, x_max, y_min,
y_max, other_positions=None, min_points=10):
positions = []
......
......@@ -11,9 +11,10 @@ import numpy as np
from dhi.config import (
poi_data, br_hh_names, campaign_labels, colors, color_sequence, marker_sequence,
)
from dhi.util import import_ROOT, to_root_latex, create_tgraph, try_int
from dhi.util import import_ROOT, to_root_latex, create_tgraph, try_int, colored
from dhi.plots.util import (
use_style, draw_model_parameters, create_hh_process_label, determine_limit_digits,
get_graph_points,
)
......@@ -107,7 +108,7 @@ def plot_limit_scan(
legend_entries = 6 * [(h_dummy, " ", "L")]
# helper to read values into graphs
def create_graph(values=expected_values, key="limit", sigma=None, insert=None):
def create_graph(values=expected_values, key="limit", sigma=None, pad=True, insert=None):
return create_tgraph(
len(values[key]),
values[scan_parameter],
......@@ -116,24 +117,26 @@ def plot_limit_scan(
0,
(values[key] - values["{}_m{}".format(key, sigma)]) if sigma else 0,
(values["{}_p{}".format(key, sigma)] - values[key]) if sigma else 0,
pad=True,
pad=pad,
insert=insert,
)
# 2 sigma band
g_2sigma = None
if "limit_p2" in expected_values and "limit_m2" in expected_values:
g_2sigma = create_graph(sigma=2)
r.setup_graph(g_2sigma, props={"LineWidth": 2, "LineStyle": 2, "FillColor": colors.yellow})
draw_objs.append((g_2sigma, "SAME,4"))
draw_objs.append((g_2sigma, "SAME,4")) # option 4 might fallback to 3, see below
legend_entries[5] = (g_2sigma, "#pm 2 #sigma expected", "LF")
y_max_value = max(y_max_value, max(expected_values["limit_p2"]))
y_min_value = min(y_min_value, min(expected_values["limit_m2"]))
# 1 sigma band
g_1sigma = None
if "limit_p1" in expected_values and "limit_m1" in expected_values:
g_1sigma = create_graph(sigma=1)
r.setup_graph(g_1sigma, props={"LineWidth": 2, "LineStyle": 2, "FillColor": colors.green})
draw_objs.append((g_1sigma, "SAME,4"))
draw_objs.append((g_1sigma, "SAME,4")) # option 4 might fallback to 3, see below
legend_entries[4] = (g_1sigma, "#pm 1 #sigma expected", "LF")
y_max_value = max(y_max_value, max(expected_values["limit_p1"]))
y_min_value = min(y_min_value, min(expected_values["limit_m1"]))
......@@ -173,6 +176,22 @@ def plot_limit_scan(
h_dummy.SetMinimum(y_min)
h_dummy.SetMaximum(y_max)
# draw option 4 of error graphs is buggy when the error point or bar is above the visible,
# vertical range of the pad, so check these cases and fallback to option 3
fallback_graph_option = False
for g in filter(bool, [g_2sigma, g_1sigma]):
_, y_values, _, _, _, y_errors_up = get_graph_points(g, errors=True)
if any((y + y_err_up) >= y_max for y, y_err_up in zip(y_values, y_errors_up)):
fallback_graph_option = True
break
if fallback_graph_option:
for i, objs in enumerate(list(draw_objs)):
if isinstance(objs, tuple) and len(objs) >= 2 and objs[0] in [g_2sigma, g_1sigma]:
draw_objs[i] = (objs[0], "SAME,3") + objs[2:]
print("{}: changed draw option of graph {} to '3' as it exceeds the vertical pad "
"range which is not supported for option '4'".format(
colored("WARNING", "yellow"), objs[0]))
# theory prediction
if has_thy:
if has_thy_err:
......
......@@ -302,17 +302,106 @@ def _get_contour(hist, level):
return contours
def get_graph_points(g):
def get_graph_points(g, errors=False):
ROOT = import_ROOT()
x_values, y_values = [], []
asym_errors = False
if errors:
if isinstance(g, ROOT.TGraphAsymmErrors):
x_errors_up, x_errors_down = [], []
y_errors_up, y_errors_down = [], []
asym_errors = True
elif isinstance(g, ROOT.TGraphErrors):
x_errors, y_errors = [], []
else:
errors = False
x, y = ROOT.Double(), ROOT.Double()
for i in range(g.GetN()):
g.GetPoint(i, x, y)
x_values.append(float(x))
y_values.append(float(y))
if asym_errors:
x_errors_up.append(g.GetErrorXhigh(i))
x_errors_down.append(g.GetErrorXlow(i))
y_errors_up.append(g.GetErrorYhigh(i))
y_errors_down.append(g.GetErrorYlow(i))
elif errors:
x_errors.append(g.GetErrorX(i))
y_errors.append(g.GetErrorY(i))
if asym_errors:
return x_values, y_values, x_errors_down, x_errors_up, y_errors_down, y_errors_up
elif errors:
return x_values, y_values, x_errors, y_errors
else:
return x_values, y_values
def repeat_graph(g, n):
points = sum((n * [p] for p in zip(*get_graph_points(g))), [])
g_repeated = g.__class__(len(points))
for i, (x, y) in enumerate(points):
g_repeated.SetPoint(i, x, y)
return x_values, y_values
return g_repeated
def invert_graph(g, x_min=None, x_max=None, y_min=None, y_max=None, x_axis=None, y_axis=None,
offset=0.):
# get all graph values
x_values, y_values = get_graph_points(g)
# get default frame values
if x_min is None:
if x_axis:
x_min = x_axis.GetXmin() - 0.01 * (x_axis.GetXmax() - x_axis.GetXmin())
else:
x_min = min(x_values)
if x_max is None:
if x_axis:
x_max = x_axis.GetXmax() + 0.01 * (x_axis.GetXmax() - x_axis.GetXmin())
else:
x_max = max(x_values)
if y_min is None:
if y_axis:
y_min = y_axis.GetXmin() - 0.01 * (y_axis.GetXmax() - y_axis.GetXmin())
else:
y_min = min(y_values)
if y_max is None:
if y_axis:
y_max = y_axis.GetXmax() + 0.01 * (y_axis.GetXmax() - y_axis.GetXmin())
else:
y_max = max(y_values)
# define outer "frame" points
bl = (x_min - offset, y_min - offset)
tl = (x_min - offset, y_max + offset)
tr = (x_max + offset, y_max + offset)
br = (x_max + offset, y_min - offset)
corners = [tr, br, bl, tl]
# find the corner that is closest to the graph start point
dist = lambda x, y: ((x - x_values[0])**2. + (y - y_values[0])**2.)**0.5
start_index = min(list(range(4)), key=lambda i: dist(corners[i][0], corners[i][1]))
# to invert the graph, create a new graph whose points start with an outer frame consisting of
# the 4 corners, the graph points itself, the first point of the graph again to close it, and
# ending with the closest corner again
points = (2 * corners)[start_index:start_index + 5]
points.extend(zip(x_values, y_values))
points.append((x_values[0], y_values[0]))
points.append(corners[start_index])
# copy the graph and fill points
g_inv = g.__class__(len(points))
for i, (x, y) in enumerate(points):
g_inv.SetPoint(i, x, y)
return g_inv
def get_text_extent(t, text_size=None, text_font=None):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment