00001
00002 import os, sys
00003 import coral
00004 import array
00005 import optparse
00006 from RecoLuminosity.LumiDB import csvSelectionParser, selectionParser
00007 import RecoLuminosity.LumiDB.lumiQueryAPI as LumiQueryAPI
00008 import re
00009
00010 from pprint import pprint
00011
00012 def fillPileupHistogram (deadTable, parameters,
00013 runNumber = 0, hist = None, debug = False,
00014 mode='deadtable'):
00015 '''Given a deadtable and run number, will (create if necessary
00016 and) fill histogram with expected pileup distribution. If a
00017 histogram is created, it is owned by the user and is his/her
00018 responsibility to clean up the memory.'''
00019 if hist:
00020 maxBin = hist.GetNbinsX()
00021 upper = int( hist.GetBinLowEdge(maxBin) + \
00022 hist.GetBinWidth(maxBin) + 0.25 )
00023 else:
00024 histname = '%s_%s' % (parameters.pileupHistName, runNumber)
00025 hist = ROOT.TH1D (histname, histname, parameters.maxPileupBin + 1,
00026 -0.5, parameters.maxPileupBin + 0.5)
00027 upper = parameters.maxPileupBin
00028 for lumiSection, deadArray in sorted (deadTable.iteritems()):
00029 if mode == 'csv':
00030 numerator = float (deadArray[1])
00031 denominator = float (deadArray[0])
00032 instLumiArray = deadArray[2]
00033 livetime = 1
00034 if numerator < 0:
00035 numerator = 0
00036 if denominator:
00037 livetime = numerator / denominator
00038 else:
00039
00040 if len(deadArray) <= parameters.xingIndex:
00041
00042
00043
00044 if parameters.noWarnings:
00045 continue
00046 if runNumber:
00047 print "No Xing Instantaneous luminosity information for run %d, lumi section %d" \
00048 % (runNumber, lumiSection)
00049 else:
00050 print "No Xing Instantaneous luminosity information for lumi section %d" \
00051 % lumiSection
00052 continue
00053 numerator = float (deadArray[0])
00054 denominator = float (deadArray[2] * deadArray[4])
00055 xingInstLumiArray = deadArray[parameters.xingIndex]
00056
00057
00058 instLumiArray = [(xingInstLumiArray[index], xingInstLumiArray[index + 1]) \
00059 for index in xrange( 0, len (xingInstLumiArray), 2 ) ]
00060 livetime = 1
00061 if numerator < 0:
00062 numerator = 0
00063 if denominator:
00064 livetime = 1 - numerator / denominator
00065
00066 for xing, xingInstLumi in instLumiArray:
00067 xingIntLumi = xingInstLumi * parameters.lumiSectionLen * livetime
00068 mean = xingInstLumi * parameters.minBiasXsec * \
00069 parameters.rotationTime
00070 if mean > 100:
00071 if runNumber:
00072 print "mean number of pileup events > 100 for run %d, lum %d : m %f l %f" % \
00073 (runNumber, lumiSection, mean, xingInstLumi)
00074 else:
00075 print "mean number of pileup events > 100 for lum %d: m %f l %f" % \
00076 (lumiSection, mean, xingInstLumi)
00077
00078 for obs in range (upper):
00079 prob = ROOT.TMath.Poisson (obs, mean)
00080 totalProb += prob
00081 hist.Fill (obs, prob * xingIntLumi)
00082 if debug:
00083 print "ls", lumiSection, "xing", xing, "inst", xingInstLumi, \
00084 "mean", mean, "totalProb", totalProb, 1 - totalProb
00085 print " hist mean", hist.GetMean()
00086 if totalProb < 1:
00087 hist.Fill (obs, (1 - totalProb) * xingIntLumi)
00088 return hist
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100 if __name__ == '__main__':
00101 parameters = LumiQueryAPI.ParametersObject()
00102 beamModeChoices = ["","stable", "quiet", "either"]
00103 parser = optparse.OptionParser ("Usage: %prog [--options] output.root",
00104 description = "Script to estimate pileup distribution using xing instantaneous luminosity information and minimum bias cross section. Output is TH1D stored in root file")
00105 dbGroup = optparse.OptionGroup (parser, "Database Options")
00106 inputGroup = optparse.OptionGroup (parser, "Input Options")
00107 pileupGroup = optparse.OptionGroup (parser, "Pileup Options")
00108 dbGroup.add_option ('--parameters', dest = 'connect', action = 'store',
00109 help = 'connect string to lumiDB (optional, default to frontier://LumiCalc/CMS_LUMI_PROD)')
00110 dbGroup.add_option ('-P', dest = 'authpath', action = 'store',
00111 help = 'path to authentication file')
00112 dbGroup.add_option ('--siteconfpath', dest = 'siteconfpath', action = 'store',
00113 help = 'specific path to site-local-config.xml file, default to $CMS_PATH/SITECONF/local/JobConfig, ' \
00114 'if path undefined, fallback to cern proxy&server')
00115 dbGroup.add_option ('--debug', dest = 'debug', action = 'store_true',
00116 help = 'debug')
00117 inputGroup.add_option ('-r', dest = 'runnumber', action = 'store',
00118 help = 'run number')
00119 inputGroup.add_option ('-i', dest = 'inputfile', action = 'store',
00120 help = 'lumi range selection file')
00121 inputGroup.add_option ('-b', dest = 'beammode', default='', choices=beamModeChoices,
00122 help = "beam mode, optional for delivered action, default ('%%default' out of %s)" % beamModeChoices)
00123 inputGroup.add_option ('--lumiversion', dest = 'lumiversion', type='string', default='0001',
00124 help = 'lumi data version, optional for all, default %default')
00125 inputGroup.add_option ('--hltpath', dest = 'hltpath', action = 'store',
00126 help = 'specific hltpath to calculate the recorded luminosity, default to all')
00127 inputGroup.add_option ('--csvInput', dest = 'csvInput', type='string', default='',
00128 help = 'Use CSV file from lumiCalc.py instead of lumiDB')
00129 pileupGroup.add_option ('--xingMinLum', dest = 'xingMinLum', type='float', default = 1e-3,
00130 help = 'Minimum luminosity considered for "lsbylsXing" action, default %default')
00131 pileupGroup.add_option ('--minBiasXsec', dest = 'minBiasXsec', type='float', default = parameters.minBiasXsec,
00132 help = 'Minimum bias cross section assumed (in mb), default %default mb')
00133 pileupGroup.add_option ('--histName', dest='histName', type='string', default = parameters.pileupHistName,
00134 help = 'Histrogram name (default %default)')
00135 pileupGroup.add_option ('--maxPileupBin', dest='maxPileupBin', type='int', default = parameters.maxPileupBin,
00136 help = 'Maximum bin in pileup histogram (default %default)')
00137 pileupGroup.add_option ('--saveRuns', dest='saveRuns', action='store_true',
00138 help = 'Save pileup histograms for individual runs')
00139 pileupGroup.add_option ('--debugLumi', dest='debugLumi',
00140 action='store_true',
00141 help = 'Print out debug info for individual lumis')
00142 pileupGroup.add_option ('--nowarning', dest = 'nowarning',
00143 action = 'store_true', default = False,
00144 help = 'suppress bad for lumi warnings' )
00145 parser.add_option_group (dbGroup)
00146 parser.add_option_group (inputGroup)
00147 parser.add_option_group (pileupGroup)
00148
00149 (options, args) = parser.parse_args()
00150 import ROOT
00151 if not args:
00152 parser.print_usage()
00153 sys.exit()
00154 if len (args) != 1:
00155 parser.print_usage()
00156 raise RuntimeError, "Exactly one output file must be given"
00157 output = args[0]
00158
00159
00160 if options.authpath:
00161 os.environ['CORAL_AUTH_PATH'] = options.authpath
00162
00163
00164 parameters.verbose = True
00165 parameters.noWarnings = options.nowarning
00166 parameters.lumiXing = True
00167 parameters.lumiversion = options.lumiversion
00168 if options.beammode=='stable':
00169 parameters.beammode = 'STABLE BEAMS'
00170 parameters.xingMinLum = options.xingMinLum
00171 parameters.minBiasXsec = options.minBiasXsec
00172 parameters.pileupHistName = options.histName
00173 parameters.maxPileupBin = options.maxPileupBin
00174
00175 session, svc = \
00176 LumiQueryAPI.setupSession (options.connect or \
00177 'frontier://LumiCalc/CMS_LUMI_PROD',
00178 options.siteconfpath, parameters,options.debug)
00179
00180
00181 if not options.inputfile and not options.runnumber and not options.csvInput:
00182 raise "must specify either a run (-r), an input run selection file (-i), or an input CSV file (--csvInput)"
00183 pileupHist = ROOT.TH1D(parameters.pileupHistName, parameters.pileupHistName,
00184 1001,
00185 0.0, 25.)
00186 histList = []
00187 if options.csvInput:
00188
00189
00190
00191 sepRE = re.compile (r'[\s,;:]+')
00192 runLumiDict = {}
00193 events = open (options.csvInput, 'r')
00194 csvDict = {}
00195 for line in events:
00196 pieces = sepRE.split (line.strip())
00197 if len (pieces) < 8:
00198 continue
00199
00200
00201
00202 try:
00203 run, lumi = int ( pieces[0] ), int ( pieces[2] )
00204 delivered, recorded = float( pieces[7] ), float( pieces[8] )
00205 xingInstLumiArray = [( int(orbit), float(lum) ) \
00206 for orbit, lum in zip( pieces[9::2],
00207 pieces[10::2] ) ]
00208 except:
00209 continue
00210 csvDict.setdefault (run, {})[lumi] = \
00211 ( delivered, recorded, xingInstLumiArray )
00212 events.close()
00213 for runNumber, lumiDict in sorted( csvDict.iteritems() ):
00214 if options.saveRuns:
00215 hist = fillPileupHistogram (lumiDict, parameters,
00216 runNumber = runNumber,
00217 debug = options.debugLumi,
00218 mode='csv')
00219 pileupHist.Add (hist)
00220 histList.append (hist)
00221 else:
00222 fillPileupHistogram (lumiDict, parameters,
00223 hist = pileupHist,
00224 debug = options.debugLumi,
00225 mode='csv')
00226
00227 histFile = ROOT.TFile.Open (output, 'recreate')
00228 if not histFile:
00229 raise RuntimeError, \
00230 "Could not open '%s' as an output root file" % output
00231 pileupHist.Write()
00232 for hist in histList:
00233 hist.Write()
00234 histFile.Close()
00235
00236 sys.exit()
00237
00238
00239
00240 if options.runnumber:
00241 inputRange = options.runnumber
00242 else:
00243 basename, extension = os.path.splitext (options.inputfile)
00244 if extension == '.csv':
00245
00246 fileparsingResult = csvSelectionParser.csvSelectionParser (options.inputfile)
00247 else:
00248 f = open (options.inputfile, 'r')
00249 inputfilecontent = f.read()
00250 inputRange = selectionParser.selectionParser (inputfilecontent)
00251 if not inputRange:
00252 print 'failed to parse the input file', options.inputfile
00253 raise
00254
00255 recordedData = LumiQueryAPI.recordedLumiForRange (session, parameters, inputRange)
00256
00257 for runDTarray in recordedData:
00258 runNumber = runDTarray[0]
00259 deadTable = runDTarray[2]
00260 if options.saveRuns:
00261 hist = fillPileupHistogram (deadTable, parameters,
00262 runNumber = runNumber,
00263 debug = options.debugLumi)
00264 pileupHist.Add (hist)
00265 histList.append (hist)
00266 else:
00267 fillPileupHistogram (deadTable, parameters,
00268 hist = pileupHist,
00269 debug = options.debugLumi)
00270 histFile = ROOT.TFile.Open (output, 'recreate')
00271 if not histFile:
00272 raise RuntimeError, \
00273 "Could not open '%s' as an output root file" % output
00274 pileupHist.Write()
00275 for hist in histList:
00276 hist.Write()
00277 histFile.Close()
00278