CMS 3D CMS Logo

estimatePileup.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from __future__ import print_function
3 import os, sys
4 import coral
5 import array
6 import optparse
7 from RecoLuminosity.LumiDB import csvSelectionParser, selectionParser
8 import RecoLuminosity.LumiDB.lumiQueryAPI as LumiQueryAPI
9 import re
10 
11 from pprint import pprint
12 import six
13 
14 def fillPileupHistogram (deadTable, parameters,
15  runNumber = 0, hist = None, debug = False,
16  mode='deadtable'):
17  '''Given a deadtable and run number, will (create if necessary
18  and) fill histogram with expected pileup distribution. If a
19  histogram is created, it is owned by the user and is his/her
20  responsibility to clean up the memory.'''
21  if hist:
22  maxBin = hist.GetNbinsX()
23  upper = int( hist.GetBinLowEdge(maxBin) + \
24  hist.GetBinWidth(maxBin) + 0.25 )
25  else:
26  histname = '%s_%s' % (parameters.pileupHistName, runNumber)
27  hist = ROOT.TH1D (histname, histname, parameters.maxPileupBin + 1,
28  -0.5, parameters.maxPileupBin + 0.5)
29  upper = parameters.maxPileupBin
30  for lumiSection, deadArray in sorted (six.iteritems(deadTable)):
31  if mode == 'csv':
32  numerator = float (deadArray[1])
33  denominator = float (deadArray[0])
34  instLumiArray = deadArray[2]
35  livetime = 1
36  if numerator < 0:
37  numerator = 0
38  if denominator:
39  livetime = numerator / denominator
40  else:
41  # we got everything from lumiDB
42  if len(deadArray) <= parameters.xingIndex:
43  # for some reason the xing instantaneous luminosity
44  # information isn't there. Print a warning and then skip
45  # it:
46  if parameters.noWarnings:
47  continue
48  if runNumber:
49  print("No Xing Instantaneous luminosity information for run %d, lumi section %d" \
50  % (runNumber, lumiSection))
51  else:
52  print("No Xing Instantaneous luminosity information for lumi section %d" \
53  % lumiSection)
54  continue
55  numerator = float (deadArray[0])
56  denominator = float (deadArray[2] * deadArray[4])
57  xingInstLumiArray = deadArray[parameters.xingIndex]
58  # here we only want the instantaneous luminosities and don't
59  # care which crosings they fall in. So we only want the odd
60  instLumiArray = [(xingInstLumiArray[index], xingInstLumiArray[index + 1]) \
61  for index in xrange( 0, len (xingInstLumiArray), 2 ) ]
62  livetime = 1
63  if numerator < 0:
64  numerator = 0
65  if denominator:
66  livetime = 1 - numerator / denominator
67  # totalInstLumi = reduce(lambda x, y: x+y, instLumiArray) # not needed
68  for xing, xingInstLumi in instLumiArray:
69  xingIntLumi = xingInstLumi * parameters.lumiSectionLen * livetime
70  mean = xingInstLumi * parameters.minBiasXsec * \
71  parameters.rotationTime
72  if mean > 100:
73  if runNumber:
74  print("mean number of pileup events > 100 for run %d, lum %d : m %f l %f" % \
75  (runNumber, lumiSection, mean, xingInstLumi))
76  else:
77  print("mean number of pileup events > 100 for lum %d: m %f l %f" % \
78  (lumiSection, mean, xingInstLumi))
79 
80  for obs in range (upper):
81  prob = ROOT.TMath.Poisson (obs, mean)
82  totalProb += prob
83  hist.Fill (obs, prob * xingIntLumi)
84  if debug:
85  print("ls", lumiSection, "xing", xing, "inst", xingInstLumi, \
86  "mean", mean, "totalProb", totalProb, 1 - totalProb)
87  print(" hist mean", hist.GetMean())
88  if totalProb < 1:
89  hist.Fill (obs, (1 - totalProb) * xingIntLumi)
90  return hist
91 
92 
93 
94 ##############################
95 ## ######################## ##
96 ## ## ################## ## ##
97 ## ## ## Main Program ## ## ##
98 ## ## ################## ## ##
99 ## ######################## ##
100 ##############################
101 
102 if __name__ == '__main__':
103  parameters = LumiQueryAPI.ParametersObject()
104  beamModeChoices = ["","stable", "quiet", "either"]
105  parser = optparse.OptionParser ("Usage: %prog [--options] output.root",
106  description = "Script to estimate pileup distribution using xing instantaneous luminosity information and minimum bias cross section. Output is TH1D stored in root file")
107  dbGroup = optparse.OptionGroup (parser, "Database Options")
108  inputGroup = optparse.OptionGroup (parser, "Input Options")
109  pileupGroup = optparse.OptionGroup (parser, "Pileup Options")
110  dbGroup.add_option ('--parameters', dest = 'connect', action = 'store',
111  help = 'connect string to lumiDB (optional, default to frontier://LumiCalc/CMS_LUMI_PROD)')
112  dbGroup.add_option ('-P', dest = 'authpath', action = 'store',
113  help = 'path to authentication file')
114  dbGroup.add_option ('--siteconfpath', dest = 'siteconfpath', action = 'store',
115  help = 'specific path to site-local-config.xml file, default to $CMS_PATH/SITECONF/local/JobConfig, ' \
116  'if path undefined, fallback to cern proxy&server')
117  dbGroup.add_option ('--debug', dest = 'debug', action = 'store_true',
118  help = 'debug')
119  inputGroup.add_option ('-r', dest = 'runnumber', action = 'store',
120  help = 'run number')
121  inputGroup.add_option ('-i', dest = 'inputfile', action = 'store',
122  help = 'lumi range selection file')
123  inputGroup.add_option ('-b', dest = 'beammode', default='', choices=beamModeChoices,
124  help = "beam mode, optional for delivered action, default ('%%default' out of %s)" % beamModeChoices)
125  inputGroup.add_option ('--lumiversion', dest = 'lumiversion', type='string', default='0001',
126  help = 'lumi data version, optional for all, default %default')
127  inputGroup.add_option ('--hltpath', dest = 'hltpath', action = 'store',
128  help = 'specific hltpath to calculate the recorded luminosity, default to all')
129  inputGroup.add_option ('--csvInput', dest = 'csvInput', type='string', default='',
130  help = 'Use CSV file from lumiCalc.py instead of lumiDB')
131  pileupGroup.add_option ('--xingMinLum', dest = 'xingMinLum', type='float', default = 1e-3,
132  help = 'Minimum luminosity considered for "lsbylsXing" action, default %default')
133  pileupGroup.add_option ('--minBiasXsec', dest = 'minBiasXsec', type='float', default = parameters.minBiasXsec,
134  help = 'Minimum bias cross section assumed (in mb), default %default mb')
135  pileupGroup.add_option ('--histName', dest='histName', type='string', default = parameters.pileupHistName,
136  help = 'Histrogram name (default %default)')
137  pileupGroup.add_option ('--maxPileupBin', dest='maxPileupBin', type='int', default = parameters.maxPileupBin,
138  help = 'Maximum bin in pileup histogram (default %default)')
139  pileupGroup.add_option ('--saveRuns', dest='saveRuns', action='store_true',
140  help = 'Save pileup histograms for individual runs')
141  pileupGroup.add_option ('--debugLumi', dest='debugLumi',
142  action='store_true',
143  help = 'Print out debug info for individual lumis')
144  pileupGroup.add_option ('--nowarning', dest = 'nowarning',
145  action = 'store_true', default = False,
146  help = 'suppress bad for lumi warnings' )
147  parser.add_option_group (dbGroup)
148  parser.add_option_group (inputGroup)
149  parser.add_option_group (pileupGroup)
150  # parse arguments
151  (options, args) = parser.parse_args()
152  import ROOT
153  if not args:
154  parser.print_usage()
155  sys.exit()
156  if len (args) != 1:
157  parser.print_usage()
158  raise RuntimeError("Exactly one output file must be given")
159  output = args[0]
160 
161  # get database session hooked up
162  if options.authpath:
163  os.environ['CORAL_AUTH_PATH'] = options.authpath
164 
165  ## Save what we need in the parameters object
166  parameters.verbose = True
167  parameters.noWarnings = options.nowarning
168  parameters.lumiXing = True
169  parameters.lumiversion = options.lumiversion
170  if options.beammode=='stable':
171  parameters.beammode = 'STABLE BEAMS'
172  parameters.xingMinLum = options.xingMinLum
173  parameters.minBiasXsec = options.minBiasXsec
174  parameters.pileupHistName = options.histName
175  parameters.maxPileupBin = options.maxPileupBin
176 
177  session, svc = \
178  LumiQueryAPI.setupSession (options.connect or \
179  'frontier://LumiCalc/CMS_LUMI_PROD',
180  options.siteconfpath, parameters,options.debug)
181 
182  ## Let's start the fun
183  if not options.inputfile and not options.runnumber and not options.csvInput:
184  raise "must specify either a run (-r), an input run selection file (-i), or an input CSV file (--csvInput)"
185  pileupHist = ROOT.TH1D(parameters.pileupHistName, parameters.pileupHistName,
186  1001,
187  0.0, 25.)
188  histList = []
189  if options.csvInput:
190  # we're going to read in the CSV file and use this as not only
191  # the selection of which run/events to use, but also the
192  # source of the lumi information.
193  sepRE = re.compile (r'[\s,;:]+')
194  runLumiDict = {}
195  events = open (options.csvInput, 'r')
196  csvDict = {}
197  for line in events:
198  pieces = sepRE.split (line.strip())
199  if len (pieces) < 8:
200  continue
201  #if len (pieces) % 2:
202  # # not an even number
203  # continue
204  try:
205  run, lumi = int ( pieces[0] ), int ( pieces[2] )
206  delivered, recorded = float( pieces[7] ), float( pieces[8] )
207  xingInstLumiArray = [( int(orbit), float(lum) ) \
208  for orbit, lum in zip( pieces[9::2],
209  pieces[10::2] ) ]
210  except:
211  continue
212  csvDict.setdefault (run, {})[lumi] = \
213  ( delivered, recorded, xingInstLumiArray )
214  events.close()
215  for runNumber, lumiDict in sorted( six.iteritems(csvDict) ):
216  if options.saveRuns:
217  hist = fillPileupHistogram (lumiDict, parameters,
218  runNumber = runNumber,
219  debug = options.debugLumi,
220  mode='csv')
221  pileupHist.Add (hist)
222  histList.append (hist)
223  else:
224  fillPileupHistogram (lumiDict, parameters,
225  hist = pileupHist,
226  debug = options.debugLumi,
227  mode='csv')
228 
229  histFile = ROOT.TFile.Open (output, 'recreate')
230  if not histFile:
231  raise RuntimeError("Could not open '%s' as an output root file" % output)
232  pileupHist.Write()
233  for hist in histList:
234  hist.Write()
235  histFile.Close()
236  #pprint (csvDict)
237  sys.exit()
238 
239 
240  ## Get input source
241  if options.runnumber:
242  inputRange = options.runnumber
243  else:
244  basename, extension = os.path.splitext (options.inputfile)
245  if extension == '.csv': # if file ends with .csv, use csv
246  # parser, else parse as json file
247  fileparsingResult = csvSelectionParser.csvSelectionParser (options.inputfile)
248  else:
249  f = open (options.inputfile, 'r')
250  inputfilecontent = f.read()
251  inputRange = selectionParser.selectionParser (inputfilecontent)
252  if not inputRange:
253  print('failed to parse the input file', options.inputfile)
254  raise
255 
256  recordedData = LumiQueryAPI.recordedLumiForRange (session, parameters, inputRange)
257  ## pprint (recordedData)
258  for runDTarray in recordedData:
259  runNumber = runDTarray[0]
260  deadTable = runDTarray[2]
261  if options.saveRuns:
262  hist = fillPileupHistogram (deadTable, parameters,
263  runNumber = runNumber,
264  debug = options.debugLumi)
265  pileupHist.Add (hist)
266  histList.append (hist)
267  else:
268  fillPileupHistogram (deadTable, parameters,
269  hist = pileupHist,
270  debug = options.debugLumi)
271  histFile = ROOT.TFile.Open (output, 'recreate')
272  if not histFile:
273  raise RuntimeError("Could not open '%s' as an output root file" % output)
274  pileupHist.Write()
275  for hist in histList:
276  hist.Write()
277  histFile.Close()
278 
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def fillPileupHistogram(deadTable, parameters, runNumber=0, hist=None, debug=False, mode='deadtable')