def choose_file(): """Get graph & fit fcn from ROOT file via tkFileDialog, plot them on the canvas""" ftypes = [('ROOT files', '*.root'), ('All files', '*')] dlg = tkFileDialog.Open(filetypes=ftypes) fl = dlg.show() if fl != '': root_file = ROOT.TFile(fl, "READ") gr = root_file.Get(graph_entry.get()) fn = root_file.Get(fn_entry.get()) if gr and fn: tkMessageBox.showinfo("Got graph & fit", "Got graph %s and function %s" % (graph_entry.get(), fn_entry.get())) # store xy points so properly drawn when canvas updated global graph_x, graph_errx, graph_y, graph_erry graph_x, graph_y = cu.get_xy(gr) graph_errx, graph_erry = cu.get_exey(gr) new_params = [fn.GetParameter(i) for i in xrange(fn.GetNumberFreeParameters())] for slider, box, param in zip(sliders, multiplier_boxes, new_params): set_slider_box_values(slider, box, param) else: if not gr: tkMessageBox.showwarning("No graph", "Graph with name %s does not exist" % graph_entry.get()) if not fn: tkMessageBox.showwarning("No function", "Function with name %s does not exist" % graph_entry.get())
def setup_fit(graph, function, absetamin, absetamax, outputfile): """Setup for fitting (auto-calculate sensible range). Returns a sub-graph of only sensible points (chop off turnover at low pT, and any high pT tail), along with a corresponding fit function whose range has been set to match the sub graph. """ print 'Setting up fit' xarr, yarr = cu.get_xy(graph) exarr, eyarr = cu.get_exey(graph) # first test out graph isn't empty if len(xarr) == 0: raise RuntimeError("graph in setup_fit() is empty") fit_max = max(xarr) # Maxmimum pt for upper bound of fit # fit_min = 10 if absetamin > 2.9 else 10 fit_min = min(xarr) # Minimum pt for lower bound of fit # For lower bound of fit, use either fit_min or the pt # of the maximum correction value, whichever has the larger pT. # Check to make sure it's not the last point on the graph # (e.g. if no turnover), in which case just use the default fit_min # Then find the index of the closest corresponding value in xarr (since # fit_min could correspond to a pT that isn't in the list of x-points) # Note that we want the maximum in the first half of the graph to avoid # the 'flick' at high pT in HF # JOE EDIT: (un)comment the next 4 lines to (not) have the flick max_corr = max(yarr[:len(yarr) / 2]) max_corr_ind = yarr.index(max_corr) max_corr_pt = xarr[max_corr_ind] fit_min = max(fit_min, max_corr_pt) if (max_corr_pt != xarr[-1]) and (max_corr_pt != xarr[-1]) else fit_min min_ind = next(i for i, x in enumerate(xarr) if x >= fit_min) # To find upper limit of fit, we need to detect if & where there is a turnover # (i.e. where the gradient goes from -ve to +ve) # This is made difficult by the fact the graph may be 'noisy' and simply # taking the gradient may not be enough (and give multiple points where # the gradient changes). To counter this, we smooth the gradient by # averaging over several points def moving_average(arr, n): """Returns a np.array of moving-averages of array arr, where each point in the rerurned array is the average of the consecutive n points in the original array. By definition, this will return an array of length len(arr) + 1 - n """ return np.array([np.mean(arr[i:i+n]) for i in range(0, len(arr) - n + 1)]) def calc_crossing(arr): """Calculate value at which value the array crosses 0. Looks at points in groups of 4, and finds the smallest group where the first 2 points < 0, and the next 2 points > 0. This ignores values which peak above 0 for 1 point. Returns the array (index, value) of the point closest to 0. """ for i in range(2, len(arr)): group = np.concatenate((-1 * arr[i-2: i], arr[i: i+2])) if np.all(group > 0): return i - 2 + list(group).index(np.min(group)), np.min(group) return None, None grad = np.gradient(yarr, 1) n_sample = 5 intercept_ind, intercept = None, None # keep incrementing the smooting value until we get a clean intercept while not intercept_ind and not intercept: x_ave = moving_average(xarr, n_sample) grad_ave = moving_average(grad, n_sample) intercept_ind, intercept = calc_crossing(grad_ave) n_sample += 1 # quit if we've got stuck if n_sample == 12: break if intercept and intercept_ind: print 'Found minima' print 'Smoothing param:', n_sample # find closest x value to intercept max_ind, fit_max = closest_element(xarr, x_ave[intercept_ind]) else: print '! Could not find minima, falling back to just using min()' # Here we assume a failure to find any minima, so fallback and use # the smallest point. max_ind = list(yarr).index(min(yarr)) fit_max = xarr[max_ind] if fit_min > fit_max: raise RuntimeError('fit_min > fit_max! (%f > %f)' % (fit_min, fit_max)) print "Correction fn fit range:", fit_min, fit_max # Generate a correction function with suitable range this_fit = function.Clone(function.GetName() + 'eta_%g_%g' % (absetamin, absetamax)) this_fit.SetRange(fit_min, fit_max) # Make a sub-graph with only the points used for fitting # Do not user graph.RemovePoint()! It doesn't work, and only removes every other point # Instead make a graph with the bit of array we want fit_graph = ROOT.TGraphErrors(max_ind + 1 - min_ind, np.array(xarr[min_ind:max_ind + 1]), np.array(yarr[min_ind:max_ind + 1]), np.array(exarr[min_ind:max_ind + 1]), np.array(eyarr[min_ind:max_ind + 1])) fit_graph.SetName(graph.GetName() + "_fit") return fit_graph, this_fit
def setup_fit(graph, function, absetamin, absetamax, outputfile): """Setup for fitting (auto-calculate sensible range). Returns a sub-graph of only sensible points (chop off turnover at low pT, and any high pT tail), along with a corresponding fit function whose range has been set to match the sub graph. """ print 'Setting up fit' xarr, yarr = cu.get_xy(graph) exarr, eyarr = cu.get_exey(graph) # first test out graph isn't empty if len(xarr) == 0: raise RuntimeError("graph in setup_fit() is empty") fit_max = max(xarr) # Maxmimum pt for upper bound of fit # fit_min = 10 if absetamin > 2.9 else 10 fit_min = min(xarr) # Minimum pt for lower bound of fit # For lower bound of fit, use either fit_min or the pt # of the maximum correction value, whichever has the larger pT. # Check to make sure it's not the last point on the graph # (e.g. if no turnover), in which case just use the default fit_min # Then find the index of the closest corresponding value in xarr (since # fit_min could correspond to a pT that isn't in the list of x-points) # Note that we want the maximum in the first half of the graph to avoid # the 'flick' at high pT in HF # JOE EDIT: (un)comment the next 4 lines to (not) have the flick max_corr = max(yarr[:len(yarr) / 2]) max_corr_ind = yarr.index(max_corr) max_corr_pt = xarr[max_corr_ind] fit_min = max(fit_min, max_corr_pt) if (max_corr_pt != xarr[-1]) and ( max_corr_pt != xarr[-1]) else fit_min min_ind = next(i for i, x in enumerate(xarr) if x >= fit_min) # To find upper limit of fit, we need to detect if & where there is a turnover # (i.e. where the gradient goes from -ve to +ve) # This is made difficult by the fact the graph may be 'noisy' and simply # taking the gradient may not be enough (and give multiple points where # the gradient changes). To counter this, we smooth the gradient by # averaging over several points def moving_average(arr, n): """Returns a np.array of moving-averages of array arr, where each point in the rerurned array is the average of the consecutive n points in the original array. By definition, this will return an array of length len(arr) + 1 - n """ return np.array( [np.mean(arr[i:i + n]) for i in range(0, len(arr) - n + 1)]) def calc_crossing(arr): """Calculate value at which value the array crosses 0. Looks at points in groups of 4, and finds the smallest group where the first 2 points < 0, and the next 2 points > 0. This ignores values which peak above 0 for 1 point. Returns the array (index, value) of the point closest to 0. """ for i in range(2, len(arr)): group = np.concatenate((-1 * arr[i - 2:i], arr[i:i + 2])) if np.all(group > 0): return i - 2 + list(group).index(np.min(group)), np.min(group) return None, None grad = np.gradient(yarr, 1) n_sample = 5 intercept_ind, intercept = None, None # keep incrementing the smooting value until we get a clean intercept while not intercept_ind and not intercept: x_ave = moving_average(xarr, n_sample) grad_ave = moving_average(grad, n_sample) intercept_ind, intercept = calc_crossing(grad_ave) n_sample += 1 # quit if we've got stuck if n_sample == 12: break if intercept and intercept_ind: print 'Found minima' print 'Smoothing param:', n_sample # find closest x value to intercept max_ind, fit_max = closest_element(xarr, x_ave[intercept_ind]) else: print '! Could not find minima, falling back to just using min()' # Here we assume a failure to find any minima, so fallback and use # the smallest point. max_ind = list(yarr).index(min(yarr)) fit_max = xarr[max_ind] ############# # JOE_HACKs # # 1. to fix the maximum pt for all fits (high pt saturation issue) # helps ease things into the right solution space # if fit_max > 650.0: # print "*** WARNING: about to apply a JOE_HACK ***" # print "*** lowers the upper limit of the fits to 650GeV ***" # fit_max = 650.0 # 2. to change the auto settings for a troublesome eta bin #if absetamin == 2.5: # print "*** WARNING: about to apply a JOE_HACK ***" # print "*** messing with fit limits for 2.500<|eta|<2.964 ***" # max_ind = 80 # fit_max = xarr[max_ind] # print max_ind # print fit_max # fit_min = 40.0 # min_ind = 0 if absetamin == 2.964: print "* WARNING: about to apply a JOE_HACK *" print "* messing with fit limits for 2.964<|eta|<3.489 *" max_ind = 17 fit_max = xarr[max_ind] print max_ind print fit_max fit_min = 40.0 min_ind = 0 if absetamin == 3.489: print "* WARNING: about to apply a JOE_HACK *" print "* messing with fit limits for 3.489<|eta|<4.191 *" max_ind = 17 fit_max = xarr[max_ind] print max_ind print fit_max fit_min = 40.0 min_ind = 0 if absetamin == 4.191: print "* WARNING: about to apply a JOE_HACK *" print "* messing with fit limits for 4.191<|eta|<5.191 *" max_ind = 17 fit_max = xarr[max_ind] print max_ind print fit_max fit_min = 40.0 min_ind = 0 ############### ############### if fit_min > fit_max: raise RuntimeError('fit_min > fit_max! (%f > %f)' % (fit_min, fit_max)) print "Correction fn fit range:", fit_min, fit_max # Generate a correction function with suitable range this_fit = function.Clone(function.GetName() + 'eta_%g_%g' % (absetamin, absetamax)) this_fit.SetRange(fit_min, fit_max) # Make a sub-graph with only the points used for fitting # Do not user graph.RemovePoint()! It doesn't work, and only removes every other point # Instead make a graph with the bit of array we want fit_graph = ROOT.TGraphErrors(max_ind + 1 - min_ind, np.array(xarr[min_ind:max_ind + 1]), np.array(yarr[min_ind:max_ind + 1]), np.array(exarr[min_ind:max_ind + 1]), np.array(eyarr[min_ind:max_ind + 1])) fit_graph.SetName(graph.GetName() + "_fit") return fit_graph, this_fit