CMS 3D CMS Logo

tree2hists.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 """
3 Create ROOT Histograms from one or more ROOT TTrees or TNtuples.
4 
5 Options are specified in the given configuration file.
6 """
7 from __future__ import print_function
8 
9 # Create configuration file:
10 # tree2hists.py
11 # Edit, then run with config file:
12 # tree2hists.py config.py
13 
14 __license__ = '''\
15 Copyright (c) 2010 Michael Anderson <mbanderson@wisc.edu>
16 
17 Permission is hereby granted, free of charge, to any person obtaining a copy
18 of this software and associated documentation files (the "Software"), to deal
19 in the Software without restriction, including without limitation the rights
20 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 copies of the Software, and to permit persons to whom the Software is
22 furnished to do so, subject to the following conditions:
23 
24 The above copyright notice and this permission notice shall be included in
25 all copies or substantial portions of the Software.
26 
27 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33 THE SOFTWARE.
34 '''
35 
36 
37 
38 import sys # For exiting program
39 if '-h' in sys.argv or '--help' in sys.argv:
40  print('''\
41 Create ROOT Histograms from one or more ROOT TTrees or TNtuples.
42 
43 Run by specifying configuration file:
44  tree2hists config.py
45 
46 Create default config file by running with no arguments:
47  tree2hists''')
48  sys.exit(0)
49 try:
50  from ROOT import TFile, TTree, TH1F, TH2F, TH3F, gROOT
51 except Exception as e:
52  print(e)
53  print ("Use a python that has PyROOT installed.")
54  sys.exit(0)
55 from copy import deepcopy # For copying histograms
56 from math import pi # For use in histogram bounds
57 from array import array # For making Float_t array ROOT wants (for hists)
58 from datetime import datetime # For output filename
59 from os import path # For finding file
60 
61 
62 
63 class RootTree:
64  """Wrapper for TTrees and TNtuples, allowing association with
65  a scale and cuts."""
66  def __init__(self, treeName, fileName, scale=1.0, cuts=""):
67  self.fileName = fileName
68  self.treeName = treeName
69  self.scale = scale
70  self.cuts = cuts
71  self.tfile = TFile()
72  self.ttree = TTree()
73 
74 class Plot:
75  """Wrapper for TH1 objects, associating TTree variables with a histogram"""
76  def __init__(self, treeVariable, histogram, cuts="", storeErrors=True):
77  self.treeVariable = treeVariable
78  self.histogram = histogram
79  self.name = histogram.GetName()
80  self.cuts = cuts
81  if storeErrors: self.histogram.Sumw2()
82 
83 def join_cuts(*list_of_cuts):
84  """Joins list of cuts (strings) into something ROOT can handle.
85  Example: given ('1<2','','5>4') returns '1<2&&5>4'"""
86  list_of_nonempty_cuts = []
87  for cut in list_of_cuts:
88  if cut:
89  list_of_nonempty_cuts.append(cut)
90  return '&&'.join(list_of_nonempty_cuts)
91 
92 def duration_to_string(start, end):
93  timeTaken = end - start
94  hours, remainder = divmod(timeTaken.seconds, 3600)
95  minutes, seconds = divmod(remainder, 60)
96  if hours>0:
97  return "%i hours, %i minutes" % (hours, minutes)
98  elif minutes>0:
99  return "%i minutes" % minutes
100  return "%i seconds" % seconds
101 
103  """Writes configuration file for tree2hists"""
104  defaultConfig = '''# Configuration file for tree2hists
105 # Created %s.
106 try:
107  ## the normal way to import from rootplot
108  from rootplot.tree2hists import RootTree, Plot
109 except ImportError:
110  ## special import for CMSSW installations of rootplot
111  from PhysicsTools.PythonAnalysis.rootplot.tree2hists import RootTree, Plot
112 from array import array # to allow making Float_t arrays for ROOT hists
113 from math import pi
114 from ROOT import TH1F, TH2F # import other kinds of hists as neeeded
115 
116 list_of_files = [RootTree("Treename", fileName="photons.root", scale=1.0, cuts=""),
117  RootTree("Treename", fileName="photons2.root", scale=1.0, cuts="")]
118 
119 output_filename = "Hists_photons.root"
120 
121 cut_for_all_files = "(!TTBit[36] && !TTBit[37] && !TTBit[38] && !TTBit[39] && !vtxIsFake && vtxNdof>4 && abs(vtxZ)<=15)"
122 
123 # All plots are made for each "cut set".
124 # A "cut set" is 3 things: folder name to store hists in, string to add to hist titles, and cuts for these hists.
125 # Let cut_sets = [] to make all plots.
126 cut_sets = [
127  ("barrel15to20", "(|#eta|<1.45, 15<E_{T}<20)", "et>15&&et<20&&abs(eta)<1.45"),
128  ("barrel20to30", "(|#eta|<1.45, 20<E_{T}<30)", "et>20&&et<30&&abs(eta)<1.45"),
129  ("endcap15to20", "(1.7<|#eta|<2.5, 15<E_{T}<20)", "et>15&&et<20&&abs(eta)>1.7&&abs(eta)<2.5"),
130  ("endcap20to30", "(1.7<|#eta|<2.5, 20<E_{T}<30)", "et>20&&et<30&&abs(eta)>1.7&&abs(eta)<2.5")
131  ]
132 
133 # Define histograms to plot
134 bins_et = array("f", [15.0, 20.0, 30.0, 50.0, 80.0, 120.0]) # example custom bins
135 list_of_plots = [
136  Plot("et" , TH1F("pho_et" , "Lead #gamma: E_{T};E_{T} (GeV);entries/bin", 25, 0.0, 100.0)),
137  Plot("eta" , TH1F("pho_eta" , "Lead #gamma: #eta;#eta;entries/bin" , 25, -3.0, 3.0)),
138  Plot("et" , TH1F("pho_et_binned" , "Lead #gamma: E_{T};E_{T} (GeV);entries/bin", len(bins_et)-1, bins_et)),
139  Plot("sigmaIetaIeta", TH1F("pho_sigmaIEtaIEta", "Lead #gamma: #sigma_{i#etai#eta};#sigma_{i#etai#eta};entries/bin",20, 0, 0.06)),
140  Plot("metEt/et" , TH1F("metEt_over_phoEt" , "MET / E_{T}(#gamma);MET/E_{T}(sc);entries/bin" , 20, 0.0, 3.0)),
141  Plot("phi:eta" , TH2F("phoPhi_vs_phoEta" , "Lead #gamma: #phi vs #eta;#eta;#phi" , 50, -2.5, 2.5, 18, -pi, pi))
142  ]
143 ''' % datetime.now().strftime("%b %d, %Y")
144  f = open('t2h_config.py', 'w')
145  f.write(defaultConfig)
146  f.close()
147  print("Created default configuration file: t2h_config.py")
148  print("Edit it, then run by typing:")
149  print(" tree2hists t2h_config.py")
150 
151 
152 def make_all_hists_all_files(list_of_RootTrees, list_of_Plots, cut_for_all_files, list_of_cutSets):
153  '''Open root files one at a time, make plots, then close them.'''
154  list_of_plots_to_write = []
155 
156  # Create plots for each set of cuts
157  for set_of_cuts in list_of_cutSets:
158  histname_fix, title_fix, current_cut_set = set_of_cuts
159  for plot in list_of_Plots:
160  new_plot = deepcopy(plot)
161  new_title = ' '.join((plot.histogram.GetTitle(), title_fix))
162  new_plot.histogram.SetTitle(new_title)
163  list_of_plots_to_write.append((new_plot, set_of_cuts))
164 
165  for j, rootTree in enumerate(list_of_RootTrees):
166  rootTree.tfile = TFile(rootTree.fileName, "read") # Open TFile
167  if rootTree.tfile.IsZombie():
168  print("Error opening %s, exiting..." % rootTree.fileName)
169  sys.exit(0)
170  try: # Try to get TTree from file.
171  rootTree.tfile.GetObject(rootTree.treeName, rootTree.ttree)
172  except:
173  print("Error: %s not found in %s, exiting..." % (rootTree.treeName,
174  rootTree.fileName))
175  sys.exit(1)
176 
177  print("\n%s: Opened %s %i MB" % (datetime.now().strftime("%I:%M%p"),
178  rootTree.fileName,
179  path.getsize(rootTree.fileName)/1048576))
180  print(" %s has %i entries, will plot with scale=%.2e, cuts='%s'" % (rootTree.treeName,
181  rootTree.ttree.GetEntries(),
182  rootTree.scale,
183  rootTree.cuts))
184 
185  # Loop over plots
186  print(" # entries var >> histogram")
187  for i, (plot, set_of_cuts) in enumerate(list_of_plots_to_write):
188  histname_fix, title_fix, current_cut_set = set_of_cuts
189  tmp_hist = plot.histogram.Clone("temp") # Create temp hist
190  all_cuts = join_cuts(cut_for_all_files, rootTree.cuts,
191  current_cut_set, plot.cuts) # Set cuts
192  rootTree.ttree.Draw( "%s >> temp" % plot.treeVariable, all_cuts,
193  "goff") # Draw with graphics off
194  tmp_hist.Scale(rootTree.scale) # Scale temp
195  entries_before = plot.histogram.GetEntries()
196  plot.histogram.Add(tmp_hist) # Add temp hist to total
197  entries_after = plot.histogram.GetEntries()
198  print(" %3i %7i %20s >> %s/%s" % (i, entries_after-entries_before,
199  plot.treeVariable, histname_fix,
200  plot.histogram.GetName()), end=' ')
201  if plot.cuts:
202  print("\textra cuts: %s" % plot.cuts, end=' ') # plot-specific cuts
203  print()
204 
205  rootTree.tfile.Close() # Close TFile
206  print("%s: Closed %s" % (datetime.now().strftime("%I:%M%p"),
207  rootTree.fileName))
208  return list_of_plots_to_write
209 
210 
211 
212 def tree2hists_main(config_file):
213  try:
214  # Import only certain variables
215  sys.path.insert(0, '')
216  _temp = __import__(config_file, globals(), locals(),
217  ['list_of_files','output_filename',
218  'cut_for_all_files','cut_sets','list_of_plots'], -1)
219  list_of_files = _temp.list_of_files
220  output_filename = _temp.output_filename
221  cut_for_all_files = _temp.cut_for_all_files
222  cut_sets = _temp.cut_sets
223  list_of_plots = _temp.list_of_plots
224  for rootTree in list_of_files:
225  if not path.isfile(rootTree.fileName):
226  print("Error:\n %s\nnot found for input." % rootTree.fileName)
227  sys.exit(1)
228  hist_names = [plot.name for plot in list_of_plots]
229  if len(hist_names)>len(set(hist_names)):
230  print(hist_names)
231  print("Error: Each plot needs a unique name, exiting...")
232  sys.exit(1)
233  if path.isfile(output_filename):
234  print("Warning: %s exists" % output_filename)
235  except Exception as e:
236  print(e)
237  print("Error with %s" % config_file)
238  sys.exit(1)
239 
240  if path.isfile('rootlogon.C'):
241  print("Loading rootlogon.C")
242  gROOT.Macro('rootlogon.C') # Load functions from rootlogon script
243 
244  if cut_sets:
245  print("\n%i defined cut sets:" % len(cut_sets))
246  for cut in cut_sets:
247  name, title_fix, current_cut_set = cut
248  print(" %s\t: '%s'" % (name, current_cut_set))
249  cut_names = [name for name,num,cut in cut_sets]
250  if len(cut_names)>len(set(cut_names)):
251  print("Error: Each cut set needs a unique name, exiting...")
252  sys.exit(1)
253  else:
254  cut_sets = [("","","")] # Make all plots, no extra cuts
255 
256  print("\nCuts to apply to all files:\n\t'%s'" % cut_for_all_files)
257 
258  start_time = datetime.now()
259  list_of_plots_to_write = make_all_hists_all_files(list_of_files,
260  list_of_plots,
261  cut_for_all_files,
262  cut_sets)
263  end_time = datetime.now()
264  print("Done drawing all plots after %s." % duration_to_string(start_time, end_time))
265 
266  # Store and save/close files
267  outputFile = TFile(output_filename, "recreate")
268  if outputFile.IsZombie():
269  print("Error opening %s for output exiting..." % output_filename)
270  sys.exit(1)
271  print("\nOpened output file. Saving histograms...")
272  outputFile.cd()
273  for set_of_cuts in cut_sets:
274  outputFile.mkdir(set_of_cuts[0])
275  print(" # entries histogram")
276  for i, (plot, cutset) in enumerate(list_of_plots_to_write):
277  outputFile.cd(cutset[0])
278  print(" %3i %7i %s/%s" % (i, plot.histogram.GetEntries(),
279  cutset[0],
280  plot.histogram.GetName()))
281  plot.histogram.Write()
282  outputFile.Close()
283  print("Done saving.")
284  print("\nScaled & added histograms from %i TTrees saved in\n %s" % (len(list_of_files), output_filename))
285 
286 
287 def main():
288  if len(sys.argv) > 1:
289  if path.isfile(sys.argv[1]):
290  config_file = sys.argv[1].split('.')[0]
291  tree2hists_main(config_file)
292  else:
293  print("%s not found." % sys.argv[1])
294  print("Create default config file by running tree2hists "
295  "with no argument.")
296  sys.exit(1)
297  else:
298  if path.exists('t2h_config.py'):
299  print("Run with specific config file, like:")
300  print(" tree2hists t2h_config.py")
301  sys.exit(1)
303  sys.exit(0)
304 
305 if __name__ == "__main__":
306  main()
def join_cuts(list_of_cuts)
Definition: tree2hists.py:83
def make_all_hists_all_files(list_of_RootTrees, list_of_Plots, cut_for_all_files, list_of_cutSets)
Definition: tree2hists.py:152
def __init__(self, treeVariable, histogram, cuts="", storeErrors=True)
Definition: tree2hists.py:76
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def __init__(self, treeName, fileName, scale=1.0, cuts="")
Definition: tree2hists.py:66
static std::string join(char **cmd)
Definition: RemoteFile.cc:19
Definition: main.py:1
def duration_to_string(start, end)
Definition: tree2hists.py:92
Define classes and generators #######################################.
Definition: tree2hists.py:63
def tree2hists_main(config_file)
Define the main program #############################################.
Definition: tree2hists.py:212