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