def reduce(self, outname): _ofile = TFile(outname, "recreate") h_sig = copy.deepcopy(self.hists['TPrime350_fitMass_4j_1t']) h_sig.SetName("signal") h_sig.SetTitle("signal") _ofile.Append(h_sig) h_sig_jesup = copy.deepcopy( self.hists['TPrime350_fitMass_4j_1t_JESup']) h_sig_jesup.SetName("signal_jesup") h_sig_jesup.SetTitle("signal_jesup") _ofile.Append(h_sig_jesup) h_sig_jesdown = copy.deepcopy( self.hists['TPrime350_fitMass_4j_1t_JESdown']) h_sig_jesdown.SetName("signal_jesdown") h_sig_jesdown.SetTitle("signal_jesdown") _ofile.Append(h_sig_jesdown) h_bg1 = copy.deepcopy(self.hists['Top_fitMass_4j_1t']) h_bg1.SetName("bg1") h_bg1.SetTitle("bg1") _ofile.Append(h_bg1) h_bg1_jesup = copy.deepcopy(self.hists['Top_fitMass_4j_1t_JESup']) h_bg1_jesup.SetName("bg1_jesup") h_bg1_jesup.SetTitle("bg1_jesup") _ofile.Append(h_bg1_jesup) h_bg1_jesdown = copy.deepcopy(self.hists['Top_fitMass_4j_1t_JESdown']) h_bg1_jesdown.SetName("bg1_jesdown") h_bg1_jesdown.SetTitle("bg1_jesdown") _ofile.Append(h_bg1_jesdown) h_bg2 = copy.deepcopy(self.hists['Wjets_fitMass_4j_1t']) h_bg2.SetName("bg2") h_bg2.SetTitle("bg2") _ofile.Append(h_bg2) h_bg2_jesup = copy.deepcopy(self.hists['Wjets_fitMass_4j_1t_JESup']) h_bg2_jesup.SetName("bg2_jesup") h_bg2_jesup.SetTitle("bg2_jesup") _ofile.Append(h_bg2_jesup) h_bg2_jesdown = copy.deepcopy( self.hists['Wjets_fitMass_4j_1t_JESdown']) h_bg2_jesdown.SetName("bg2_jesdown") h_bg2_jesdown.SetTitle("bg2_jesdown") _ofile.Append(h_bg2_jesdown) #h_data = copy.deepcopy(self.hists['Data_fitMass_4j_1t']) h_data = self.generate_hist(h_bg1, 'mass', 52) h_data.Add(self.generate_hist(h_bg2, 'mass', 10)) #h_data.Add(self.generate_hist(h_sig, 'mass', 5)) h_data.Print() h_data.SetName("data") h_data.SetTitle("data") _ofile.Append(h_data) _ofile.Write() _ofile.Close() return
class PlotGraphs: def __init__(self, data, xlow, xhigh, ylow, yhigh, xlabel = "", ylabel = "", xLegend = .45, yLegend = .60, legendWidth = 0.20, legendHeight = 0.45, fillStyle = 3395, drawOption = 'APL3', make_tfile = False): self.graph_index = {} self.ngraphs = 0 self.data = data self.drawOption = drawOption self.xlow=xlow self.xhigh=xhigh self.ylow=ylow self.yhigh=yhigh self.xlabel = xlabel self.ylabel = ylabel self.multigraph=TMultiGraph("MultiGraph", "") self.legend_type = {} if make_tfile: self.tfile = TFile('tgraphs.root', 'recreate') self.legend = TLegend(xLegend, yLegend, xLegend + legendWidth, yLegend + legendHeight) self.g_ = {} self.g2_ = {} self.g3_ = {} for cat in data.cat: if data.type[cat] == 'observed': #self.g_[cat] = TGraph(len(data.x[cat]), data.x[cat], data.y[cat]) self.g_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl[cat], data.exh[cat], data.eyl[cat], data.eyh[cat]) self.g_[cat].SetName(cat) if make_tfile: _gcopy = self.g_[cat].Clone() self.tfile.Append(_gcopy) if data.dofit[cat]: #self.g_[cat].Fit("pol3", "M", "", data.fit_min[cat], data.fit_max[cat]) f1 = TF1("f1", "[0]+[1]*(x-1000.0)/1000.0+[2]*(x-1000.0)*(x-1000.0)/1000000.0+[3]*(x-1000.0)*(x-1000.0)*(x-1000.0)/1000000000.0", data.fit_min[cat], data.fit_max[cat]) f2 = TF1("f2", "[0]+[1]*(x-1000.0)/1000.0+[2]*(x-1000.0)*(x-1000.0)/1000000.0", data.fit_min[cat], data.fit_max[cat]) f3 = TF1("f3", "[0]+[1]*(x-1000.0)/1000.0", data.fit_min[cat], data.fit_max[cat]) _fr = self.g_[cat].Fit("f2", "MEWS", "", data.fit_min[cat], data.fit_max[cat]) _fr.Print() elif data.type[cat] == 'expected': self.g_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl[cat], data.exh[cat], data.eyl[cat], data.eyh[cat]) self.g2_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl2[cat], data.exh2[cat], data.eyl2[cat], data.eyh2[cat]) self.g3_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl2[cat], data.exh2[cat], data.y[cat], data.exl2[cat]) self.g3_[cat].SetFillStyle(1002) #self.g3_[cat].SetFillStyle(3008) # 95% quantile self.g2_[cat].SetMarkerColor(data.fill2_color[cat]) self.g2_[cat].SetMarkerStyle(data.marker_style[cat]) self.g2_[cat].SetMarkerSize(data.marker_size[cat]) self.g2_[cat].SetLineColor(data.fill2_color[cat]) self.g2_[cat].SetLineStyle(data.line_style[cat]) self.g2_[cat].SetLineWidth(data.line_width[cat]) #self.g2_[cat].SetFillColor(data.marker_color[cat]+2) self.g2_[cat].SetFillColor(data.fill2_color[cat]) #self.g2_[cat].SetFillStyle(3008) #self.g2_[cat].SetFillStyle(3003) self.g2_[cat].SetFillStyle(data.fill2_style[cat]) self.g_[cat].SetMarkerColor(data.marker_color[cat]) self.g_[cat].SetMarkerStyle(data.marker_style[cat]) self.g_[cat].SetMarkerSize(data.marker_size[cat]) self.g_[cat].SetLineColor(data.line_color[cat]) self.g_[cat].SetLineStyle(data.line_style[cat]) self.g_[cat].SetLineWidth(data.line_width[cat]) self.g_[cat].SetFillColor(data.fill_color[cat]) if data.fill_style[cat] == None: self.g_[cat].SetFillStyle(fillStyle) else: self.g_[cat].SetFillStyle(data.fill_style[cat]) if data.type[cat] == 'observed': # only the main observed limit is a line, # everything else is a filled area if cat[0:3] == 'obs': _legend_type = 'lp' if 'PC' in drawOption: _draw_option = 'PC' else: _draw_option = 'PL' self.multigraph.Add(self.g_[cat], _draw_option) elif cat[0:3] == 'SSM': _legend_type = 'lp' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) # print theory curve #self.g_[cat].Print() elif cat[0:3] == 'Psi': _legend_type = 'lp' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) elif cat[0:2] == 'RS': _legend_type = 'f' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) else: _legend_type = 'f' self.multigraph.Add(self.g_[cat]) #self.multigraph.Add(self.g_[cat]) self.graph_index[cat] = self.ngraphs self.ngraphs += 1 #self.legend . AddEntry( self.g_[cat], data.tlegend[cat], _legend_type); self.legend_type[cat] = _legend_type elif data.type[cat] == 'expected': self.g3_[cat].SetFillColor(0) self.g3_[cat].SetMinimum(self.ylow) self.g3_[cat].SetMaximum(self.yhigh) self.g3_[cat].GetXaxis().SetLimits(self.xlow, self.xhigh) self.multigraph.Add(self.g3_[cat], 'C3') self.ngraphs += 1 # 95 expected band self.multigraph.Add(self.g2_[cat]) self.ngraphs += 1 self.multigraph.Add(self.g_[cat], 'C3L') self.graph_index[cat] = self.ngraphs self.ngraphs += 1 # median line gline = self.g_[cat].Clone() gline.SetLineWidth(3) gline.SetLineColor(ROOT.kBlue) gline.SetLineStyle(2) # print graph contents #gline.Print() #self.multigraph.Add(gline, 'LXC') self.multigraph.Add(gline, 'C3X') self.legend . AddEntry( gline, 'median expected', "l"); self.ngraphs += 1 #self.legend . AddEntry( self.g_[cat], data.tlegend[cat], "f"); self.legend . AddEntry( self.g_[cat], '68% expected', "f"); # 95% expected band legend self.legend . AddEntry( self.g2_[cat], '95% expected', "f"); keylist = data.legend_index.keys() # keys are indices keylist.sort() for key in keylist: self.legend . AddEntry( self.g_[data.legend_index[key]], data.tlegend[data.legend_index[key]], self.legend_type[data.legend_index[key]]); self.legend . SetShadowColor(0) self.legend . SetFillColor(0) self.legend . SetLineColor(0) if make_tfile: self.tfile.Write() self.tfile.Close() def draw(self, yLabelSize = 0.045): self.multigraph.SetMinimum(self.ylow) self.multigraph.SetMaximum(self.yhigh) self.multigraph . Draw(self.drawOption) self.multigraph.GetXaxis().SetNdivisions(405) self.multigraph.GetYaxis().SetNdivisions(405) self.multigraph.GetXaxis().SetLimits(self.xlow, self.xhigh) #self.multigraph.GetYaxis().SetTitle("") #self.multigraph.GetYaxis().SetLabelSize(yLabelSize) #self.multigraph.GetXaxis().SetLabelSize(yLabelSize) # axis labels #latex = TLatex() #latex.SetNDC() ##latex.SetTextSize(0.04) #latex.SetTextSize(yLabelSize) #latex.SetTextAlign(31) # align right #latex.DrawLatex(0.95,0.01, self.xlabel) #latex.SetTextAngle(90) #latex.DrawLatex(0.04,0.95, self.ylabel) XLabel(self.xlabel, 0.95, 0.03, text_size = 0.07) YLabel(self.ylabel, 0.04,0.9, text_size = 0.07) self.legend.SetFillStyle(0) self.legend.SetBorderSize(0) #self.legend.SetTextSize(0.04) self.legend.SetTextSize(yLabelSize) self.legend.SetTextFont(22) self.legend . Draw() return self.multigraph def draw_line(self, xline, ymin = -0.02, ymax = 0.20): if xline == None: return #print 'XXXX', xline _x = array('d') _y = array('d') _x.append(xline) _y.append(ymin) _x.append(float(xline)) _y.append(ymax) g_ = TGraph(len(_x), _x, _y) self.multigraph.Add(g_, 'L') def print_values(self, cat1, cat2): legend = 'PlotGraphs::print_values():' if cat1 in self.data.cat: graph1 = self.multigraph.GetListOfGraphs().At(self.graph_index[cat1]) else: return None if cat2 in self.data.cat: graph2 = self.multigraph.GetListOfGraphs().At(self.graph_index[cat2]) else: return None for x in range (750, 1150, 50): dmax = 0.0 drelmax = 0.0 v1 = graph1.Eval(x) v2 = graph2.Eval(x) d = math.fabs(v2-v1) drel = math.fabs((v2-v1)/v1) if d>dmax: dmax = d if drel>drelmax: drelmax = drel print legend, 'x =', x print legend, cat1, 'value =', v1 print legend, cat2, 'value =', v2 print legend, 'abs diff =', d print legend, 'abs relative diff =', drel print legend, 'max abs diff =', dmax print legend, 'max abs relative diff =', drelmax def find_intersection(self, cat1, cat2, xmin = 250, xmax = 2500, precision = 0.00001): legend = 'PlotGraphs::find_intersection():' if cat1 in self.data.cat: graph1 = self.multigraph.GetListOfGraphs().At(self.graph_index[cat1]) else: return None if cat2 in self.data.cat: graph2 = self.multigraph.GetListOfGraphs().At(self.graph_index[cat2]) #graph2.Print() else: return None _x = xmin print legend, graph1.Eval(_x) - graph2.Eval(_x) _d = graph1.Eval(_x) - graph2.Eval(_x) _step = xmax - xmin while abs(_d) > precision: _x += _step if _x > xmax or _x < xmin: return None _d2 = graph1.Eval(_x) - graph2.Eval(_x) _sign1 = _d > 0 _sign2 = _d2 > 0 if _sign1 != _sign2: _step = -0.5*_step _d = _d2 if abs(_d2) < precision: return _x
class PlotGraphs: def __init__(self, data, xlow, xhigh, ylow, yhigh, xlabel="", ylabel="", xLegend=.45, yLegend=.60, legendWidth=0.20, legendHeight=0.45, fillStyle=3395, drawOption='APL3', make_tfile=False): self.graph_index = {} self.ngraphs = 0 self.data = data self.drawOption = drawOption self.xlow = xlow self.xhigh = xhigh self.ylow = ylow self.yhigh = yhigh self.xlabel = xlabel self.ylabel = ylabel self.multigraph = TMultiGraph("MultiGraph", "") self.legend_type = {} if make_tfile: self.tfile = TFile('tgraphs.root', 'recreate') self.legend = TLegend(xLegend, yLegend, xLegend + legendWidth, yLegend + legendHeight) self.g_ = {} self.g2_ = {} self.g3_ = {} for cat in data.cat: if data.type[cat] == 'observed': #self.g_[cat] = TGraph(len(data.x[cat]), data.x[cat], data.y[cat]) self.g_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl[cat], data.exh[cat], data.eyl[cat], data.eyh[cat]) self.g_[cat].SetName(cat) if make_tfile: _gcopy = self.g_[cat].Clone() self.tfile.Append(_gcopy) if data.dofit[cat]: #self.g_[cat].Fit("pol3", "M", "", data.fit_min[cat], data.fit_max[cat]) f1 = TF1( "f1", "[0]+[1]*(x-1000.0)/1000.0+[2]*(x-1000.0)*(x-1000.0)/1000000.0+[3]*(x-1000.0)*(x-1000.0)*(x-1000.0)/1000000000.0", data.fit_min[cat], data.fit_max[cat]) f2 = TF1( "f2", "[0]+[1]*(x-1000.0)/1000.0+[2]*(x-1000.0)*(x-1000.0)/1000000.0", data.fit_min[cat], data.fit_max[cat]) f3 = TF1("f3", "[0]+[1]*(x-1000.0)/1000.0", data.fit_min[cat], data.fit_max[cat]) _fr = self.g_[cat].Fit("f2", "MEWS", "", data.fit_min[cat], data.fit_max[cat]) _fr.Print() elif data.type[cat] == 'expected': self.g_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl[cat], data.exh[cat], data.eyl[cat], data.eyh[cat]) self.g2_[cat] = TGraphAsymmErrors( len(data.x[cat]), data.x[cat], data.y[cat], data.exl2[cat], data.exh2[cat], data.eyl2[cat], data.eyh2[cat]) self.g3_[cat] = TGraphAsymmErrors(len(data.x[cat]), data.x[cat], data.y[cat], data.exl2[cat], data.exh2[cat], data.y[cat], data.exl2[cat]) self.g3_[cat].SetFillStyle(1002) #self.g3_[cat].SetFillStyle(3008) # 95% quantile self.g2_[cat].SetMarkerColor(data.fill2_color[cat]) self.g2_[cat].SetMarkerStyle(data.marker_style[cat]) self.g2_[cat].SetMarkerSize(data.marker_size[cat]) self.g2_[cat].SetLineColor(data.fill2_color[cat]) self.g2_[cat].SetLineStyle(data.line_style[cat]) self.g2_[cat].SetLineWidth(data.line_width[cat]) #self.g2_[cat].SetFillColor(data.marker_color[cat]+2) self.g2_[cat].SetFillColor(data.fill2_color[cat]) #self.g2_[cat].SetFillStyle(3008) #self.g2_[cat].SetFillStyle(3003) self.g2_[cat].SetFillStyle(data.fill2_style[cat]) self.g_[cat].SetMarkerColor(data.marker_color[cat]) self.g_[cat].SetMarkerStyle(data.marker_style[cat]) self.g_[cat].SetMarkerSize(data.marker_size[cat]) self.g_[cat].SetLineColor(data.line_color[cat]) self.g_[cat].SetLineStyle(data.line_style[cat]) self.g_[cat].SetLineWidth(data.line_width[cat]) self.g_[cat].SetFillColor(data.fill_color[cat]) if data.fill_style[cat] == None: self.g_[cat].SetFillStyle(fillStyle) else: self.g_[cat].SetFillStyle(data.fill_style[cat]) if data.type[cat] == 'observed': # only the main observed limit is a line, # everything else is a filled area if cat[0:3] == 'obs': _legend_type = 'lp' if 'PC' in drawOption: _draw_option = 'PC' else: _draw_option = 'PL' self.multigraph.Add(self.g_[cat], _draw_option) elif cat[0:3] == 'SSM': _legend_type = 'lp' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) # print theory curve #self.g_[cat].Print() elif cat[0:3] == 'Psi': _legend_type = 'lp' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) elif cat[0:3] == 'Stu': _legend_type = 'lp' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) elif cat[0:2] == 'RS': _legend_type = 'f' _draw_option = '3' self.multigraph.Add(self.g_[cat], _draw_option) else: _legend_type = 'f' self.multigraph.Add(self.g_[cat]) #self.multigraph.Add(self.g_[cat]) self.graph_index[cat] = self.ngraphs self.ngraphs += 1 #self.legend . AddEntry( self.g_[cat], data.tlegend[cat], _legend_type); self.legend_type[cat] = _legend_type elif data.type[cat] == 'expected': self.g3_[cat].SetFillColor(0) self.g3_[cat].SetMinimum(self.ylow) self.g3_[cat].SetMaximum(self.yhigh) self.g3_[cat].GetXaxis().SetLimits(self.xlow, self.xhigh) self.multigraph.Add(self.g3_[cat], 'C3') self.ngraphs += 1 # 95 expected band self.multigraph.Add(self.g2_[cat], 'C4L') self.ngraphs += 1 self.multigraph.Add(self.g_[cat], 'C4L') self.graph_index[cat] = self.ngraphs self.ngraphs += 1 # median line gline = self.g_[cat].Clone() gline.SetLineWidth(3) gline.SetLineColor(ROOT.kBlue) gline.SetLineStyle(2) # median expected marker gline.SetMarkerStyle(8) gline.SetMarkerSize(1) # print graph contents #gline.Print() #self.multigraph.Add(gline, 'LXC') self.multigraph.Add(gline, 'C3X') self.ngraphs += 1 # median expected legend #self.legend . AddEntry( gline, 'median expected', "l"); self.legend_type[cat] = "l" _g = self.g_[cat] self.g_[cat] = gline # 68% expected band legend #self.legend . AddEntry( self.g_[cat], '68% expected', "f"); self.legend_type[cat + '1sig'] = "f" self.g_[cat + '1sig'] = _g # 95% expected band legend #self.legend . AddEntry( self.g2_[cat], '95% expected', "f"); self.legend_type[cat + '2sig'] = "f" self.g_[cat + '2sig'] = self.g2_[cat] keylist = data.legend_index.keys() # keys are indices keylist.sort() for key in keylist: print key, data.legend_index[key] self.legend.AddEntry(self.g_[data.legend_index[key]], data.tlegend[data.legend_index[key]], self.legend_type[data.legend_index[key]]) self.legend.SetShadowColor(0) self.legend.SetFillColor(0) self.legend.SetLineColor(0) if make_tfile: self.tfile.Write() self.tfile.Close() def draw(self, yLabelSize=0.055): self.multigraph.SetMinimum(self.ylow) self.multigraph.SetMaximum(self.yhigh) self.multigraph.Draw(self.drawOption) self.multigraph.GetXaxis().SetNdivisions(405) self.multigraph.GetYaxis().SetNdivisions(405) self.multigraph.GetXaxis().SetLimits(self.xlow, self.xhigh) #self.multigraph.GetYaxis().SetTitle("") self.multigraph.GetYaxis().SetLabelSize(yLabelSize) self.multigraph.GetXaxis().SetLabelSize(yLabelSize) latex = TLatex() latex.SetNDC() #latex.SetTextSize(0.04) latex.SetTextSize(yLabelSize) latex.SetTextAlign(31) # align right latex.DrawLatex(0.95, 0.01, self.xlabel) latex.SetTextAngle(90) latex.DrawLatex(0.03, 0.9, self.ylabel) self.legend.SetFillStyle(0) self.legend.SetBorderSize(0) #self.legend.SetTextSize(0.04) #self.legend.SetTextSize(yLabelSize) self.legend.SetTextSize(yLabelSize * 0.7) self.legend.SetTextFont(42) self.legend.SetTextAlign(11) self.legend.Draw() return self.multigraph def draw_line(self, xline, ymin=-0.02, ymax=0.20): if xline == None: return #print 'XXXX', xline _x = array('d') _y = array('d') _x.append(xline) _y.append(ymin) _x.append(float(xline)) _y.append(ymax) g_ = TGraph(len(_x), _x, _y) self.multigraph.Add(g_, 'L') def print_values(self, cat1, cat2): legend = 'PlotGraphs::print_values():' if cat1 in self.data.cat: graph1 = self.multigraph.GetListOfGraphs().At( self.graph_index[cat1]) else: return None if cat2 in self.data.cat: graph2 = self.multigraph.GetListOfGraphs().At( self.graph_index[cat2]) else: return None for x in range(750, 1150, 50): dmax = 0.0 drelmax = 0.0 v1 = graph1.Eval(x) v2 = graph2.Eval(x) d = math.fabs(v2 - v1) drel = math.fabs((v2 - v1) / v1) if d > dmax: dmax = d if drel > drelmax: drelmax = drel print legend, 'x =', x print legend, cat1, 'value =', v1 print legend, cat2, 'value =', v2 print legend, 'abs diff =', d print legend, 'abs relative diff =', drel print legend, 'max abs diff =', dmax print legend, 'max abs relative diff =', drelmax def find_intersection(self, cat1, cat2, xmin=350, xmax=3500, precision=0.000000001): legend = 'PlotGraphs::find_intersection(%s,%s):' % (cat1, cat2) #always put "exp" or "obs" in cat1 if cat1 is "exp" or cat1 is "obs": if cat1 in self.data.cat: graph1 = self.multigraph.GetListOfGraphs().At( self.graph_index[cat1]) else: print "Failed to get ", cat1 return ["None"] else: print cat1, " is not obs/exp limit." return ["None"] if cat2 in self.data.cat: graph2 = self.multigraph.GetListOfGraphs().At( self.graph_index[cat2]) #graph2.Print() else: print "Failed to get ", cat2 return ["None"] _x = xmin print legend, graph1.Eval(_x) - graph2.Eval(_x) if graph1.Eval(_x) > graph2.Eval(_x): result = ["lowlimit"] else: result = [] _interval = 1 _sign1 = graph1.Eval(_x) > graph2.Eval(_x) while _x < xmax: _x += _interval _sign2 = graph1.Eval(_x) > graph2.Eval(_x) if _sign1 != _sign2: _d = 100000 _step = -0.5 * _interval _crosspt = _x while abs(_d) > precision: _crosspt += _step _d = graph1.Eval(_crosspt) - graph2.Eval(_crosspt) _sign2 = _d > 0 if _sign1 != _sign2: _step = -0.5 * abs(_step) else: _step = 0.5 * abs(_step) _sign1 = not (_sign1) result.append(_crosspt) if result: print "Allowed region:", _x = 0 while _x < len(result): if _x + 1 < len(result): print "[", result[_x], ",", result[_x + 1], "];", else: if _x + 1 == len(result): print "[", result[_x], ", inf ];" if _x + 2 == len(result): print _x += 2 else: print "[", xmin, "~", xmax, "] is excluded." del graph1 del graph2 return result