ATLAS Offline Software
Loading...
Searching...
No Matches
roofit/ROOTUtils.py
Go to the documentation of this file.
1# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
2
3# $Id: ROOTUtils.py 759047 2016-07-01 00:45:13Z beringer $
4"""
5Miscellaneous utilities for PyROOT.
6"""
7__author__ = 'Juerg Beringer'
8__version__ = '$Id: ROOTUtils.py 759047 2016-07-01 00:45:13Z beringer $'
9
10
11import ROOT
12from array import array
13
14
15protectedObjectList = []
16
17def protect(obj):
18 """Utility function to prevent ROOT objects from being garbage-collected,
19 (thus disappearing from plots) when they go out of scope."""
20 protectedObjectList.append(obj)
21 return obj
22
23
24# Define a set of useful canvas sizes for different purposes
25myCanvasSizes = { 'default': (700, 500),
26 'default2d': (545, 500),
27 'slide1': (900,560),
28 'slide2': (450,560),
29 'slide2text': (400,500),
30 'fig1': (700, 500),
31 'fig2': (1400, 500),
32 'page': (750,1000),
33 'landscape': (1600,1000),
34 'wide': (1000,500),
35 'extrawide': (1500,500),
36 'square': (700,700)
37 }
38
39
40class MyCanvas(ROOT.TCanvas):
41 """Class MyCanvas is a ROOT TCanvas that allows choosing one of
42 several typically used canvas sizes. It can automatically save
43 the canvas in different formats upon destruction provided the
44 user makes sure that all histograms still exist."""
45
46 # Class defaults
47 saveAsList = [ '.gif' ]
48 autoSaveOnExit = False
49 autoName = '%s.autosave%s'
50
51 def __init__(self,name='MyCanvas', size='default', xdivs=1, ydivs=1, saveAsList=None, autoSaveOnExit=None):
52 super(MyCanvas,self).__init__(name,name,myCanvasSizes[size][0],myCanvasSizes[size][1])
53 self.name = name
54 if saveAsList!=None:
55 self.saveAsList = saveAsList
56 else:
57 self.saveAsList = MyCanvas.saveAsList
58 if autoSaveOnExit!=None:
59 self.autoSaveOnExit = autoSaveOnExit
60 else:
61 self.autoSaveOnExit = MyCanvas.autoSaveOnExit
62 self.autoName = MyCanvas.autoName
63 self.Divide(xdivs,ydivs)
64
65 def __del__(self):
66 if self.autoSaveOnExit:
67 self.save()
68
69 def save(self):
70 for o in self.saveAsList:
71 if o[0]=='.':
72 self.SaveAs(self.autoName % (self.name,o))
73 else:
74 self.SaveAs(o)
75
76
78 """PlotLibrary is a base class that can be used to manage a set of
79 plots. Each individual plot should be created as a method of a
80 derived class. Other methods of the derived class that do not
81 generate plots should either have names starting with
82 underscore, or should be declared when calling __init__ of the
83 base class."""
84
85 def __init__(self, name = 'MyPlots', otherMethods=[]):
86 """Constructor. otherMethods specifies a list of methods of the derived
87 class that are not creating plots and should not be called by plot()."""
88 self.name = name
89 self.otherMethods = []
90 self.otherMethods.extend(otherMethods)
91 self.rootObjects = []
92
93 # Variables used by genPlot
94 self.whatList = []
95 self.singleCanvasSize = 'default'
96 self.allCanvasSize = 'page'
97 self.allCanvasDivs = (3,4)
98 self.saveAsList = []
100
101 def protect(self,obj):
102 """Protect ROOT object from garbage collection."""
103 self.rootObjects.append(obj)
104 return obj
105
106 def plot(self, plotName='',*args):
107 """Make one or all (if plotName=='') plots."""
108 if plotName:
109 self.__class__.__dict__[plotName](self,*args)
110 else:
111 for i in self.__class__.__dict__:
112 if i[0]=='_': continue # skip private methods
113 if i in self.otherMethods: continue
114 self.__class__.__dict__[i](self,*args)
115
116 def genPlot(self,what='ALL',code='plot',labels=[],*args):
117 """Make plots using a general code. genPlot makes either a single plot defined
118 by argument what, or all plots (if what=''). The plots made in the latter
119 case are specified by whatList. A canvas that is subdivided if necessary
120 is created before the plotting code is called."""
121 if what=='' or what.upper()=='ALL':
122 c = self.protect( MyCanvas('%s-%s-%s' % (self.name,what,code),
123 self.allCanvasSize,self.allCanvasDivs[0],self.allCanvasDivs[1]) )
124 iCanvas = 0
125 for w in self.whatList:
126 iCanvas += 1
127 c.cd(iCanvas)
128 try:
129 self.__class__.__dict__[code](self,w,*args)
130 except:
131 self.__class__.__bases__[0].__dict__[code](self,w,*args)
132 ROOT.gPad.Update()
133 # For some strange reason, this works only for .eps, but not e.g. for gif files...???
134 for o in self.gPadSaveAsList:
135 if o[0]=='.':
136 ROOT.gPad.SaveAs('%s-%s-%s%s' % (self.name,code,w,o))
137 else:
138 ROOT.gPad.SaveAs(o)
139
140 # If we want to put some labels on an empty pad, add them now.
141 if labels!=[] and self.allCanvasDivs[0]*self.allCanvasDivs[1] > len(self.whatList):
142 iCanvas+=1
143 c.cd(iCanvas)
144 xtext=0.0
145 ytext=0.8
146 for label in labels:
147 drawText(xtext,ytext,0.06,label)
148 ytext=ytext-0.1
149 elif labels!=[]:
150 print "ERROR: can't add labels unless we have an empty pad to use. Ignoring labels."
151
152 for o in self.saveAsList:
153 if o[0]=='.':
154 c.SaveAs('%s-%s-%s%s' % (self.name,code,what,o))
155 else:
156 c.SaveAs(o)
157
158 else:
159 c = self.protect( MyCanvas(what,self.singleCanvasSize) )
160 try:
161 self.__class__.__dict__[code](self,what,*args)
162 except:
163 self.__class__.__bases__[0].__dict__[code](self,what,*args)
164 ROOT.gPad.Update()
165 for o in self.saveAsList:
166 if o[0]=='.':
167 c.SaveAs('%s-%s-%s%s' % (self.name,code,what,o))
168 else:
169 c.SaveAs(o)
170
172 """StyleFactory is a helper class for assigning marker styles, line styles,
173 and colors in ROOT plots."""
174
175 markerSequence = [ 8, 21, 22, 23, 29, 4, 25, 26, 27, 28, 30, 2, 3, 6]
176 lineSequence = [ 1, 2, 3, 4]
177 colorSequence = [ 1, 2, 4, 6, 8]
178
179 def __init__(self,incMarker=True,incLine=True,incColor=True):
180 self.idMarker = -1
181 self.idLine = -1
182 self.idColor = -1
183 self.incMarker = 1 if incMarker else 0
184 self.incLine = 1 if incLine else 0
185 self.incColor = 1 if incColor else 0
186
187 def nextStyle(self):
188 """Get next style cycling through markers, lines and colors as specified by
189 the flags in the constructor. Returns a triplet of (marker,line,color) styles."""
190 self.idMarker += self.incMarker
191 self.idLine += self.incLine
192 self.idColor += self.incColor
193 return (StyleFactory.markerSequence[self.idMarker % len(StyleFactory.markerSequence)],
194 StyleFactory.lineSequence[self.idLine % len(StyleFactory.lineSequence)],
195 StyleFactory.colorSequence[self.idColor % len(StyleFactory.colorSequence)])
196
198 """Get next marker style."""
199 self.idMarker +=1
200 return StyleFactory.markerSequence[self.idMarker % len(StyleFactory.markerSequence)]
201
202 def nextLineStyle(self):
203 """Get next line style."""
204 self.idLine +=1
205 return StyleFactory.lineSequence[self.idLine % len(StyleFactory.lineSequence)]
206
207 def nextColorStyle(self):
208 """Get next color."""
209 self.idColor +=1
210 return StyleFactory.colorSequence[self.idColor % len(StyleFactory.colorSequence)]
211
212
213def drawAxisFrame(xmin,xmax,ymin,ymax,title='',xTitleOffset=None,yTitleOffset=None,doPlot=True,protectFrame=True):
214 frame = ROOT.TH2F('axisFrame',title,100,xmin,xmax,100,ymin,ymax)
215 frame.SetStats(False)
216 if xTitleOffset!=None:
217 frame.GetXaxis().SetTitleOffset(xTitleOffset)
218 if yTitleOffset!=None:
219 frame.GetYaxis().SetTitleOffset(yTitleOffset)
220 if doPlot:
221 frame.Draw()
222 if protectFrame:
223 protect(frame)
224 return frame
225
226
227def drawHorizontalBand(xmin,xmax,y,ywidth,color=33,protectBand=True,centralLine=False):
228 """Draw a horizontal band of width +-ywidth. Nice colors are 19 and 33."""
229 b = ROOT.TH1F('band','band',1,xmin,xmax)
230 b.SetBinContent(1,y)
231 b.SetBinError(1,ywidth)
232 b.SetFillColor(color)
233 b.SetMarkerColor(color)
234 b.SetMarkerSize(0.01)
235 b.Draw('SAMEe2')
236 if centralLine:
237 b.Draw('SAMEL')
238 if protectBand:
239 protect(b)
240 return b
241
242
243def drawText(x=0.74,y=0.87,dy=0.06,text='',font=62,color=1,align=11,linesep=';'):
244 """Draw a variable number of lines of text."""
245 t = ROOT.TLatex()
246 t.SetNDC()
247 t.SetTextFont(font)
248 t.SetTextColor(color)
249 t.SetTextAlign(align)
250 t.SetTextSize(dy/1.2)
251 for line in text.split(linesep):
252 t.DrawLatex(x,y,line)
253 y = y-dy
254 # Seems TLatex text doesn't need to be protected - why not?
255 return t
256
257
258#def drawLegend(x=0.18,y=0.9,s=0.07,legendList=[],fillColor=0,lineColor=0):
259def drawLegend(x1,y1,x2,y2,legendList=[],fillColor=0,lineColor=0,textSize=None,protectLegend=True):
260 """Draw a legend with one or more entries. legendList is a list of lists,
261 where each inner list defines the object, text and option of a legend entry.
262 NOTE: Must store returned TLegend, otherwise legend will disappear!"""
263 nlines = len(legendList)
264 if not nlines:
265 print "ERROR: drawLegend called w/o any legend entries"
266 #maxchar = 0
267 #for e in legendList:
268 # maxchar = max(len(e[1]),maxchar)
269 #l = ROOT.TLegend(x,y-nlines*s,x+(2+maxchar)*s/3.7,y)
270 l = ROOT.TLegend(x1,y1,x2,y2)
271 l.SetFillColor(fillColor)
272 l.SetLineColor(lineColor)
273 l.SetShadowColor(lineColor)
274 if textSize:
275 l.SetTextSize(textSize)
276 for e in legendList:
277 l.AddEntry(e[0],e[1],e[2])
278 l.Draw()
279 if protectLegend:
280 protect(l)
281 return l
282
283
284def moveStats(h,dx,dy,xw=0,yw=0,label=''):
285 if not h:
286 return
287 ROOT.gPad.Update()
288 st = h.GetListOfFunctions().FindObject('stats')
289 if not st:
290 print 'ERROR: No stats found - cannot move it'
291 return
292 st.SetTextColor(h.GetLineColor())
293 st.SetX1NDC(st.GetX1NDC()+dx)
294 st.SetY1NDC(st.GetY1NDC()+dy)
295 st.SetX2NDC(st.GetX2NDC()+xw if xw!=0 else st.GetX2NDC()+dx)
296 st.SetY2NDC(st.GetY2NDC()+yw if yw!=0 else st.GetY2NDC()+dy)
297 if not label:
298 h.SetName(label)
299 ROOT.gPad.Modified()
300 ROOT.gPad.Update
301
302
303def atlasLabel(x,y,isPreliminary=False,color=1,offset=0.115,isForApproval=False,energy=8,customstring="",size=0.05):
304 if x==None or y==None:
305 print "Must set (x,y) position using --atlasx and --atlasy to add labels. No ATLAS labels created."
306 return
307 offset = offset/0.05*size
308 l = ROOT.TLatex()
309 l.SetNDC()
310 l.SetTextFont(72)
311 l.SetTextSize(size)
312 l.SetTextColor(color)
313 l.DrawLatex(x,y,"ATLAS")
314
315 p = ROOT.TLatex()
316 p.SetNDC()
317 p.SetTextFont(42)
318 p.SetTextSize(size)
319 p.SetTextColor(color)
320
321 if customstring != "":
322 p.DrawLatex(x+offset,y,customstring)
323 else:
324 if (isPreliminary):
325 p.DrawLatex(x+offset,y,"Preliminary")
326 if (isForApproval):
327 #p.DrawLatex(x+offset,y,"Internal for approval")
328 p.DrawLatex(x+offset,y,"Internal")
329
330 if energy:
331 if float(energy)<15:
332 p.DrawLatex(x, y-(0.07/0.05*size), "#sqrt{s} = %s TeV" % energy)
333 else:
334 p.DrawLatex(x, y-(0.07/0.05*size), "#sqrt{s} = %s GeV" % energy)
335
336
337
338def atlasStyle(protectStyle=True):
339 s = ROOT.TStyle('ATLAS','ATLAS Style V2.02')
340
341 # use plain black on white colors
342 icol=0 # WHITE
343 s.SetFrameBorderMode(icol);
344 s.SetFrameFillColor(icol);
345 s.SetCanvasBorderMode(icol);
346 s.SetCanvasColor(icol);
347 s.SetPadBorderMode(icol);
348 s.SetPadColor(icol);
349 s.SetStatColor(icol);
350 #s.SetFillColor(icol); # don't use: white fill color floa *all* objects
351
352 # set the paper & margin sizes
353 s.SetPaperSize(20,26);
354 s.SetPadTopMargin(0.05);
355 s.SetPadRightMargin(0.05);
356 s.SetPadBottomMargin(0.16);
357 s.SetPadLeftMargin(0.16);
358
359 # set title offsets (for axis label)
360 s.SetTitleXOffset(1.4)
361 s.SetTitleYOffset(1.4)
362
363 # use large fonts
364 #Int_t font=72; # Helvetica italics
365 font = 42 # Helvetica
366 tsize = 0.05
367 s.SetTextFont(font);
368
369 s.SetTextSize(tsize);
370 s.SetLabelFont(font,"x");
371 s.SetTitleFont(font,"x");
372 s.SetLabelFont(font,"y");
373 s.SetTitleFont(font,"y");
374 s.SetLabelFont(font,"z");
375 s.SetTitleFont(font,"z");
376
377 s.SetLabelSize(tsize,"x");
378 s.SetTitleSize(tsize,"x");
379 s.SetLabelSize(tsize,"y");
380 s.SetTitleSize(tsize,"y");
381 s.SetLabelSize(tsize,"z");
382 s.SetTitleSize(tsize,"z");
383
384 # use bold lines and markers
385 s.SetMarkerStyle(20);
386 s.SetMarkerSize(1.2);
387 s.SetHistLineWidth(2);
388 s.SetLineStyleString(2,"[12 12]"); # postscript dashes
389
390 # get rid of X error bars and y error bar caps
391 #s.SetErrorX(0.001);
392
393 # get rid of error bar caps
394 s.SetEndErrorSize(0.);
395
396 # do not display any of the standard histogram decorations
397 s.SetOptTitle(0);
398 #s.SetOptStat(1111);
399 s.SetOptStat(0);
400 #s.SetOptFit(1111);
401 s.SetOptFit(0);
402
403 # put tick marks on top and RHS of plots
404 s.SetPadTickX(1);
405 s.SetPadTickY(1);
406
407 if protectStyle:
408 protect(s)
409
410 return s
411
412
413def setStyle(style = None):
414 if not style:
415 style = atlasStyle()
416 print '\nApplying style %s (%s) ...\n' % (style.GetName(),style.GetTitle())
417 ROOT.gROOT.SetStyle(style.GetName())
418 ROOT.gROOT.ForceStyle()
__init__(self, name='MyCanvas', size='default', xdivs=1, ydivs=1, saveAsList=None, autoSaveOnExit=None)
genPlot(self, what='ALL', code='plot', labels=[], *args)
__init__(self, name='MyPlots', otherMethods=[])
__init__(self, incMarker=True, incLine=True, incColor=True)
atlasLabel(x, y, isPreliminary=False, color=1, offset=0.115, isForApproval=False, energy=8, customstring="", size=0.05)
setStyle(style=None)
drawText(x=0.74, y=0.87, dy=0.06, text='', font=62, color=1, align=11, linesep=';')
moveStats(h, dx, dy, xw=0, yw=0, label='')
drawAxisFrame(xmin, xmax, ymin, ymax, title='', xTitleOffset=None, yTitleOffset=None, doPlot=True, protectFrame=True)
atlasStyle(protectStyle=True)
drawLegend(x1, y1, x2, y2, legendList=[], fillColor=0, lineColor=0, textSize=None, protectLegend=True)
drawHorizontalBand(xmin, xmax, y, ywidth, color=33, protectBand=True, centralLine=False)