CMS 3D CMS Logo

create_public_pileup_plots.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 ######################################################################
4 ## File: create_public_pileup_plots.py
5 ######################################################################
6 
7 # NOTE: Typical way to create the pileup ROOT file from the cached txt
8 # files (maintained by Mike Hildreth):
9 # pileupCalc.py -i /afs/cern.ch/cms/CAF/CMSCOMM/COMM_DQM/certification/Collisions12/8TeV/DCSOnly/json_DCSONLY.txt \
10 # --inputLumiJSON=/afs/cern.ch/cms/CAF/CMSCOMM/COMM_DQM/certification/Collisions12/8TeV/PileUp/pileup_latest.txt \
11 # --calcMode true --maxPileupBin=40 pu2012DCSONLY.root
12 
13 from __future__ import print_function
14 from builtins import range
15 import sys
16 import os
17 import commands
18 import math
19 import optparse
20 import ConfigParser
21 
22 import matplotlib
23 matplotlib.use('Agg')
24 from matplotlib import pyplot as plt
25 # FIX FIX FIX
26 # This fixes a well-know bug with stepfilled logarithmic histograms in
27 # Matplotlib.
28 from RecoLuminosity.LumiDB.mpl_axes_hist_fix import hist
29 if matplotlib.__version__ != '1.0.1':
30  print("ERROR The %s script contains a hard-coded bug-fix " \
31  "for Matplotlib 1.0.1. The Matplotlib version loaded " \
32  "is %s" % (__file__, matplotlib.__version__), file=sys.stderr)
33  sys.exit(1)
34 matplotlib.axes.Axes.hist = hist
35 # FIX FIX FIX end
36 
37 from ROOT import gROOT
38 gROOT.SetBatch(True)
39 from ROOT import PyConfig
40 PyConfig.IgnoreCommandLineOptions = True
41 from ROOT import TFile
42 
43 from RecoLuminosity.LumiDB.public_plots_tools import ColorScheme
44 from RecoLuminosity.LumiDB.public_plots_tools import LatexifyUnits
45 from RecoLuminosity.LumiDB.public_plots_tools import AddLogo
46 from RecoLuminosity.LumiDB.public_plots_tools import InitMatplotlib
47 from RecoLuminosity.LumiDB.public_plots_tools import RoundAwayFromZero
48 from RecoLuminosity.LumiDB.public_plots_tools import SavePlot
49 from RecoLuminosity.LumiDB.public_plots_tools import FONT_PROPS_SUPTITLE
50 from RecoLuminosity.LumiDB.public_plots_tools import FONT_PROPS_TITLE
51 from RecoLuminosity.LumiDB.public_plots_tools import FONT_PROPS_AX_TITLE
52 from RecoLuminosity.LumiDB.public_plots_tools import FONT_PROPS_TICK_LABEL
53 
54 try:
55  import debug_hook
56  import pdb
57 except ImportError:
58  pass
59 
60 ######################################################################
61 
62 def TweakPlot(fig, ax, add_extra_head_room=False):
63 
64  # Fiddle with axes ranges etc.
65  ax.relim()
66  ax.autoscale_view(False, True, True)
67  for label in ax.get_xticklabels():
68  label.set_ha("right")
69  label.set_rotation(30.)
70 
71  # Bit of magic here: increase vertical scale by one tick to make
72  # room for the legend.
73  if add_extra_head_room:
74  y_ticks = ax.get_yticks()
75  (y_min, y_max) = ax.get_ylim()
76  is_log = (ax.get_yscale() == "log")
77  y_max_new = y_max
78  if is_log:
79  tmp = y_ticks[-1] / y_ticks[-2]
80  y_max_new = y_max * math.pow(tmp, add_extra_head_room)
81  else:
82  tmp = y_ticks[-1] - y_ticks[-2]
83  y_max_new = y_max + add_extra_head_room * tmp
84  ax.set_ylim(y_min, y_max_new)
85 
86  # Add a second vertical axis on the right-hand side.
87  ax_sec = ax.twinx()
88  ax_sec.set_ylim(ax.get_ylim())
89  ax_sec.set_yscale(ax.get_yscale())
90 
91  for ax_tmp in fig.axes:
92  for sub_ax in [ax_tmp.xaxis, ax_tmp.yaxis]:
93  for label in sub_ax.get_ticklabels():
94  label.set_font_properties(FONT_PROPS_TICK_LABEL)
95 
96  if is_log:
97  fig.subplots_adjust(top=.89, bottom=.125, left=.11, right=.925)
98  else:
99  fig.subplots_adjust(top=.89, bottom=.125, left=.1, right=.925)
100 
101  # End of TweakPlot().
102 
103 ######################################################################
104 
105 if __name__ == "__main__":
106 
107  desc_str = "This script creates the official CMS pileup plots " \
108  "based on the output from the pileupCalc.py script."
109  arg_parser = optparse.OptionParser(description=desc_str)
110  arg_parser.add_option("--ignore-cache", action="store_true",
111  help="Ignore all cached PU results " \
112  "and run pileupCalc. " \
113  "(Rebuilds the cache as well.)")
114  (options, args) = arg_parser.parse_args()
115  if len(args) != 1:
116  print("ERROR Need exactly one argument: a config file name", file=sys.stderr)
117  sys.exit(1)
118  config_file_name = args[0]
119  ignore_cache = options.ignore_cache
120 
121  cfg_defaults = {
122  "pileupcalc_flags" : "",
123  "color_schemes" : "Joe, Greg",
124  "verbose" : False
125  }
126  cfg_parser = ConfigParser.SafeConfigParser(cfg_defaults)
127  if not os.path.exists(config_file_name):
128  print("ERROR Config file '%s' does not exist" % config_file_name, file=sys.stderr)
129  sys.exit(1)
130  cfg_parser.read(config_file_name)
131 
132  # Location of the cached ROOT file.
133  cache_file_dir = cfg_parser.get("general", "cache_dir")
134 
135  # Which color scheme to use for drawing the plots.
136  color_scheme_names_tmp = cfg_parser.get("general", "color_schemes")
137  color_scheme_names = [i.strip() for i in color_scheme_names_tmp.split(",")]
138  # Flag to turn on verbose output.
139  verbose = cfg_parser.getboolean("general", "verbose")
140 
141  # Some details on how to invoke pileupCalc.
142  pileupcalc_flags_from_cfg = cfg_parser.get("general", "pileupcalc_flags")
143  input_json = cfg_parser.get("general", "input_json")
144  input_lumi_json = cfg_parser.get("general", "input_lumi_json")
145 
146  # Some things needed for titles etc.
147  particle_type_str = cfg_parser.get("general", "particle_type_str")
148  year = int(cfg_parser.get("general", "year"))
149  cms_energy_str = cfg_parser.get("general", "cms_energy_str")
150 
151  ##########
152 
153  # Tell the user what's going to happen.
154  print("Using configuration from file '%s'" % config_file_name)
155  print("Using color schemes '%s'" % ", ".join(color_scheme_names))
156  print("Using additional pileupCalc flags from configuration: '%s'" % \
157  pileupcalc_flags_from_cfg)
158  print("Using input JSON filter: %s" % input_json)
159  print("Using input lumi JSON filter: %s" % input_lumi_json)
160 
161  ##########
162 
164 
165  ##########
166 
167  # First run pileupCalc.
168  tmp_file_name = os.path.join(cache_file_dir,"pileup_calc_tmp.root")
169  if ignore_cache:
170  cmd = "pileupCalc.py -i %s --inputLumiJSON=%s %s %s" % \
171  (input_json, input_lumi_json,
172  pileupcalc_flags_from_cfg, tmp_file_name)
173  print("Running pileupCalc (this may take a while)")
174  if verbose:
175  print(" pileupCalc cmd: '%s'" % cmd)
176  (status, output) = commands.getstatusoutput(cmd)
177  if status != 0:
178  print("ERROR Problem running pileupCalc: %s" % output, file=sys.stderr)
179  sys.exit(1)
180 
181  ##########
182 
183  in_file = TFile.Open(tmp_file_name, "READ")
184  if not in_file or in_file.IsZombie():
185  print("ERROR Could not read back pileupCalc results", file=sys.stderr)
186  sys.exit(1)
187  pileup_hist = in_file.Get("pileup")
188  pileup_hist.SetDirectory(0)
189  in_file.Close()
190 
191  ##########
192 
193  # And this is where the plotting starts.
194  print("Drawing things...")
195  ColorScheme.InitColors()
196 
197  # Turn the ROOT histogram into a Matplotlib one.
198  bin_edges = [pileup_hist.GetBinLowEdge(i) \
199  for i in range(1, pileup_hist.GetNbinsX() + 1)]
200  vals = [pileup_hist.GetBinCenter(i) \
201  for i in range(1, pileup_hist.GetNbinsX() + 1)]
202  weights = [pileup_hist.GetBinContent(i) \
203  for i in range(1, pileup_hist.GetNbinsX() + 1)]
204  # NOTE: Convert units to /pb!
205  weights = [1.e-6 * i for i in weights]
206 
207  # Loop over all color schemes.
208  for color_scheme_name in color_scheme_names:
209 
210  print(" color scheme '%s'" % color_scheme_name)
211 
212  color_scheme = ColorScheme(color_scheme_name)
213  color_line_pileup = color_scheme.color_line_pileup
214  color_fill_pileup = color_scheme.color_fill_pileup
215  logo_name = color_scheme.logo_name
216  file_suffix = color_scheme.file_suffix
217 
218  fig = plt.figure()
219 
220  for type in ["lin", "log"]:
221  is_log = (type == "log")
222  log_setting = False
223  if is_log:
224  min_val = min(weights)
225  exp = RoundAwayFromZero(math.log10(min_val))
226  log_setting = math.pow(10., exp)
227 
228  fig.clear()
229  ax = fig.add_subplot(111)
230 
231  ax.hist(vals, bins=bin_edges, weights=weights, log=log_setting,
232  histtype="stepfilled",
233  edgecolor=color_line_pileup,
234  facecolor=color_fill_pileup)
235 
236  # Set titles and labels.
237  fig.suptitle(r"CMS Average Pileup, " \
238  "%s, %d, $\mathbf{\sqrt{s} =}$ %s" % \
239  (particle_type_str, year, cms_energy_str),
240  fontproperties=FONT_PROPS_SUPTITLE)
241  ax.set_xlabel(r"Mean number of interactions per crossing",
242  fontproperties=FONT_PROPS_AX_TITLE)
243  ax.set_ylabel(r"Recorded Luminosity (%s/%.2f)" % \
244  (LatexifyUnits("pb^{-1}"),
245  pileup_hist.GetBinWidth(1)),
246  fontproperties=FONT_PROPS_AX_TITLE)
247 
248  # Add the average pileup number to the top right.
249  ax.text(.95, .925, r"<$\mathbf{\mu}$> = %.0f" % \
250  round(pileup_hist.GetMean()),
251  transform = ax.transAxes,
252  horizontalalignment="right",
253  fontproperties=FONT_PROPS_AX_TITLE)
254 
255  # Add the logo.
256  AddLogo(logo_name, ax)
257  TweakPlot(fig, ax, True)
258 
259  log_suffix = ""
260  if is_log:
261  log_suffix = "_log"
262  SavePlot(fig, "pileup_%s_%d%s%s" % \
263  (particle_type_str, year,
264  log_suffix, file_suffix))
265 
266  plt.close()
267 
268  ##########
269 
270  print("Done")
271 
272 ######################################################################
def LatexifyUnits(units_in)
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
def TweakPlot(fig, ax, add_extra_head_room=False)
T min(T a, T b)
Definition: MathUtil.h:58
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def AddLogo(logo_name, ax, zoom=1.2)
def SavePlot(fig, file_name_base)