CMS 3D CMS Logo

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