3 from __future__
import print_function
4 from __future__
import division
5 from builtins
import zip
6 from builtins
import object
7 from past.utils
import old_div
8 from builtins
import range
12 from pprint
import pprint
18 sepRE = re.compile (
r'[\s,;:]+')
19 nonSpaceRE = re.compile (
r'\S')
29 lastSingleXingRun = 136175
30 lumiSectionLength = 23.310779
40 pieces = sepRE.split (line.strip())
43 raise RuntimeError(
"Odd number of pieces")
45 raise RuntimeError(
"Not enough pieces")
47 self.
run = int (pieces[0])
48 self.
lumi = int (pieces[1])
52 raise RuntimeError(
"Pieces not right format")
55 for xing, lum
in zip (pieces[4::2],pieces[5::2]):
62 raise RuntimeError(
"Inst Lumi Info malformed")
73 raise RuntimeError(
"This event %s already has Xing information" \
75 if self.
run > LumiInfo.lastSingleXingRun:
83 old_div(self.
delivered, LumiInfo.lumiSectionLength)
96 return "%6d, %4d: %6.1f (%4.1f%%) %4.2f (%3d)" % \
114 print(
"loading luminosity information from '%s'." % filename)
115 source = open (filename,
'r')
117 'delivered',
'recorded']
134 lumi = LumiInfo (line)
137 self[lumi.key] = lumi
139 if not self.
xingInfo and lumi.xingInfo:
146 val = getattr (lumi, key)
147 if val < self.
_min[key]
or self.
_min[key] < 0:
149 if val > self.
_max[key]
or not self.
_max[key]:
168 for lumis
in self.values():
169 lumis.delivered /= lumFactor
170 lumis.recorded /= lumFactor
178 retval =
'run, lum del ( dt ) inst (#xng)\n'
179 for key, value
in sorted (six.iteritems(self)):
180 retval +=
"%s\n" % value
185 return self.
_min[key]
189 return self.
_max[key]
193 return sorted (dict.keys (self))
197 return sorted (dict.iteritems (self))
203 for key, lumi
in six.iteritems(self):
204 total += lumi.recorded
205 lumi.totalRecorded = total
206 lumi.fracRecorded = old_div(total, self.
totalRecLum)
213 for key, lumi
in six.iteritems(self):
214 if not lumi.xingInfo
and not lumi.fixXingInfo():
216 print(
"Do not have lumi xing info for %s" % lumi.keyString)
218 print(
"Setting no Xing info flag")
222 xingKeyList.append( (lumi.aveInstLum, key) )
223 if lumi.aveInstLum > maxAveInstLum:
224 maxAveInstLum = lumi.aveInstLum
227 for tup
in xingKeyList:
229 total += lumi.recorded
230 lumi.totalAXILrecorded = total
231 lumi.fracAXILrecorded = old_div(total, self.
totalRecLum)
232 lumi.fracAXIL = old_div(lumi.aveInstLum, maxAveInstLum)
243 print(
"loading events from '%s'" % filename)
244 events = open (filename,
'r')
245 runIndex, lumiIndex, eventIndex, weightIndex = 0, 1, 2, 3
247 lumiIndex, eventIndex = 2,1
253 pieces = sepRE.split (line.strip())
254 if len (pieces) < minPieces:
255 if nonSpaceRE.search (line):
256 print(
"skipping", line)
259 run, lumi, event =
int( pieces[runIndex] ), \
260 int( pieces[lumiIndex] ), \
261 int( pieces[eventIndex] )
267 print(
"Warning, %s is not found in the lumi information" \
271 raise RuntimeError(
"%s is not found in lumi information. Use '--ignoreNoLumiEvents' option to ignore these events and continue." \
273 if options.edfMode !=
'time' and not cont[key].xingInfo:
275 print(
"Warning, %s does not have Xing information" \
279 raise RuntimeError(
"%s does not have Xing information. Use '--ignoreNoLumiEvents' option to ignore these events and continue." \
282 weight = float (pieces[weightIndex])
285 eventsDict.setdefault( key, []).
append( (event, weight) )
286 totalWeight += weight
288 return eventsDict, totalWeight
291 def makeEDFplot (lumiCont, eventsDict, totalWeight, outputFile, options):
302 if 'time' == options.edfMode:
304 if lumiCont.minRun
or lumiCont.minIntLum:
310 for key, eventList
in sorted( six.iteritems(eventsDict) ):
313 if lumiCont.minRun
and lumiCont.minRun > key[0]
or \
314 lumiCont.maxRun
and lumiCont.maxRun < key[0]:
316 for event
in eventList:
320 factor = old_div(weight, totalWeight)
322 intLum = lumiCont[key].totalRecorded
324 raise RuntimeError(
"key %s not found in lumi information" \
326 if lumiCont.minIntLum
and lumiCont.minIntLum > intLum
or \
327 lumiCont.maxIntLum
and lumiCont.maxIntLum < intLum:
329 lumFrac = old_div(intLum, lumiCont.totalRecLum)
330 xVals.append( lumiCont[key].totalRecorded)
331 yVals.append (factor)
332 expectedVals.append (lumFrac)
333 predVals.append (lumFrac * options.pred)
335 if not lumiCont.maxRun
and not lumiCont.maxIntLum:
336 xVals.append (lumiCont.totalRecLum)
338 expectedVals.append (1)
339 predVals.append (options.pred)
343 if options.resetExpected:
344 slope = old_div((yVals[-1] - yVals[0]), (xVals[-1] - xVals[0]))
345 print(
"slope", slope)
346 for index, old
in enumerate (expectedVals):
347 expectedVals[index] = yVals[0] + \
348 slope * (xVals[index] - xVals[0])
352 if options.breakExpectedIntLum:
353 breakExpectedIntLum = []
354 for chunk
in options.breakExpectedIntLum:
355 pieces = sepRE.split (chunk)
358 breakExpectedIntLum.append(
float(piece) )
360 raise RuntimeError(
"'%s' from '%s' is not a valid float" \
362 breakExpectedIntLum.sort()
366 for index, xPos
in enumerate (xVals):
367 if xPos > breakExpectedIntLum[breakIndex]:
368 boundaries.append (index)
369 while breakIndex < len (breakExpectedIntLum):
371 if breakIndex >= len (breakExpectedIntLum):
377 if xPos <= breakExpectedIntLum[breakIndex]:
383 raise RuntimeError(
"No values of 'breakExpectedIntLum' are in current range.")
386 boundaries.insert (0, 0)
389 if boundaries[-1] != len (xVals) - 1:
390 boundaries.append( len (xVals) - 1 )
391 rangeList = list(zip (boundaries, boundaries[1:]))
392 for thisRange
in rangeList:
395 slope = old_div((yVals[upper] - yVals[lower]), \
396 (xVals[upper] - xVals[lower]))
397 print(
"slope", slope)
400 for index
in range (lower, upper + 1):
401 newExpected = yVals[lower] + \
402 slope * (xVals[index] - xVals[lower])
403 pairList.append( (xVals[index], newExpected) )
404 expectedVals[index] = newExpected
405 expectedChunks.append (pairList)
409 elif 'instLum' == options.edfMode
or 'instIntLum' == options.edfMode:
411 if not lumiCont.xingInfo:
412 raise RuntimeError(
"Luminosity Xing information missing.")
413 for key, eventList
in sorted( six.iteritems(eventsDict) ):
416 instLum = lumi.aveInstLum
417 fracAXIL = lumi.fracAXILrecorded
418 totalAXIL = lumi.totalAXILrecorded
420 raise RuntimeError(
"key %s not found in lumi information" \
422 for event
in eventList:
423 eventTupList.append( (instLum, fracAXIL, totalAXIL, key,
424 event[0], event[1], ) )
426 for eventTup
in eventTupList:
427 weight += eventTup[5]
428 factor = old_div(weight, totalWeight)
429 if 'instLum' == options.edfMode:
430 xVals.append (eventTup[0])
432 xVals.append (eventTup[2])
433 yVals.append (factor)
434 expectedVals.append (eventTup[1])
435 predVals.append (eventTup[1] * options.pred)
437 raise RuntimeError(
"It looks like Charles screwed up if you are seeing this.")
440 step = int (old_div(math.sqrt(size), 2) + 1)
441 if options.printValues:
442 for index
in range (size):
443 print(
"%8f %8f %8f" % (xVals[index], yVals[index], expectedVals[index]), end=
' ')
445 denom = xVals[index] - xVals[index - step]
446 numer = yVals[index] - yVals[index - step]
448 print(
" %8f" % (old_div(numer, denom)), end=
' ')
449 if 0 == index % step:
450 print(
" **", end=
' ')
455 xArray = array.array (
'd', xVals)
456 yArray = array.array (
'd', yVals)
457 expected = array.array (
'd', expectedVals)
458 graph = ROOT.TGraph( size, xArray, yArray)
459 graph.SetTitle (options.title)
460 graph.SetMarkerStyle (20)
461 expectedGraph = ROOT.TGraph( size, xArray, expected)
462 expectedGraph.SetLineColor (ROOT.kRed)
463 expectedGraph.SetLineWidth (3)
464 if options.noDataPoints:
465 expectedGraph.SetLineStyle (2)
469 print(
"average weight per event:", old_div(weight, ( size - 1)))
470 maxDistance = ROOT.TMath.KolmogorovTest (size, yArray,
473 prob = ROOT.TMath.KolmogorovProb( maxDistance * math.sqrt( size ) )
476 ROOT.gROOT.SetStyle(
'Plain')
477 ROOT.gROOT.SetBatch()
479 graph.GetXaxis().SetRangeUser (min (xVals), max (xVals))
480 minValue = min (
min(yVals),
min(expected))
482 minValue = min (minValue, min (predVals))
483 graph.GetYaxis().SetRangeUser (minValue,
484 max (
max(yVals),
max(expected),
max(predVals)))
485 graph.SetLineWidth (3)
486 if options.noDataPoints:
490 if 'instLum' == options.edfMode:
491 graph.GetXaxis().SetTitle (
"Average Xing Inst. Luminosity (1/ub/s)")
492 graph.GetXaxis().SetRangeUser (0., lumiCont.max(
'aveInstLum'))
494 if 'instIntLum' == options.edfMode:
495 graph.GetXaxis().SetTitle (
"Integrated Luminosity - Inst. Lum. Ordered (1/%s)" \
498 graph.GetXaxis().SetTitle (
"Integrated Luminosity (1/%s)" \
500 graph.GetYaxis().SetTitle (
"Fraction of Events Seen")
503 for index, chunk
in enumerate (expectedChunks):
504 expectedXarray = array.array (
'd', [item[0]
for item
in chunk])
505 expectedYarray = array.array (
'd', [item[1]
for item
in chunk])
506 expectedGraph = ROOT.TGraph( len(chunk),
509 expectedGraph.SetLineWidth (3)
510 if options.noDataPoints:
511 expectedGraph.SetLineStyle (2)
513 expectedGraph.SetLineColor (ROOT.kBlue)
515 expectedGraph.SetLineColor (ROOT.kRed)
516 expectedGraph.Draw(
"L")
517 expectedGraphs.append (expectedGraph)
518 exptectedGraph = expectedGraphs[0]
520 expectedGraph.Draw (
"L")
523 predArray = array.array (
'd', predVals)
524 green = ROOT.TGraph (size, xArray, predArray)
525 green.SetLineWidth (3)
526 green.SetLineColor (8)
528 legend = ROOT.TLegend(0.15, 0.65, 0.50, 0.85)
529 legend.SetFillStyle (0)
530 legend.SetLineColor(ROOT.kWhite)
531 observed =
'Observed'
533 observed +=
' (weighted)'
534 legend.AddEntry(graph, observed,
"PL")
535 if options.resetExpected:
536 legend.AddEntry(expectedGraph,
"Expected from partial yield",
"L")
538 legend.AddEntry(expectedGraph,
"Expected from total yield",
"L")
540 legend.AddEntry(green, options.predLabel,
"L")
541 legend.AddEntry(
"",
"D_{stat}=%.3f, N=%d" % (maxDistance, size),
"")
542 legend.AddEntry(
"",
"P_{KS}=%.3f" % prob,
"")
546 c1.Print (outputFile)
557 if __name__ ==
'__main__':
561 allowedEDF = [
'time',
'instLum',
'instIntLum']
562 parser = optparse.OptionParser (
"Usage: %prog [options] lumi.csv events.txt output.png", description=
'Script for generating EDF curves. See https://twiki.cern.ch/twiki/bin/viewauth/CMS/SWGuideGenerateEDF for more details.')
563 plotGroup = optparse.OptionGroup (parser,
"Plot Options")
564 rangeGroup = optparse.OptionGroup (parser,
"Range Options")
565 inputGroup = optparse.OptionGroup (parser,
"Input Options")
566 modeGroup = optparse.OptionGroup (parser,
"Mode Options")
567 plotGroup.add_option (
'--title', dest=
'title', type=
'string',
568 default =
'Empirical Distribution Function',
569 help =
'title of plot (default %default)')
570 plotGroup.add_option (
'--predicted', dest=
'pred', type=
'float',
572 help =
'factor by which predicted curve is greater than observed')
573 plotGroup.add_option (
'--predLabel', dest=
'predLabel', type=
'string',
574 default =
'Predicted',
575 help =
'label of predicted in legend')
576 plotGroup.add_option (
'--noDataPoints', dest=
'noDataPoints',
578 help=
"Draw lines but no points for data")
579 rangeGroup.add_option (
'--minRun', dest=
'minRun', type=
'int', default=0,
580 help=
'Minimum run number to consider')
581 rangeGroup.add_option (
'--maxRun', dest=
'maxRun', type=
'int', default=0,
582 help=
'Maximum run number to consider')
583 rangeGroup.add_option (
'--minIntLum', dest=
'minIntLum', type=
'float', default=0,
584 help=
'Minimum integrated luminosity to consider')
585 rangeGroup.add_option (
'--maxIntLum', dest=
'maxIntLum', type=
'float', default=0,
586 help=
'Maximum integrated luminosity to consider')
587 rangeGroup.add_option (
'--resetExpected', dest=
'resetExpected',
589 help=
'Reset expected from total yield to highest point considered')
590 rangeGroup.add_option (
'--breakExpectedIntLum', dest=
'breakExpectedIntLum',
591 type=
'string', action=
'append', default=[],
592 help=
'Break expected curve into pieces at integrated luminosity boundaries')
593 inputGroup.add_option (
'--ignoreNoLumiEvents', dest=
'ignore',
595 help =
'Ignore (with a warning) events that do not have a lumi section')
596 inputGroup.add_option (
'--noWarnings', dest=
'noWarnings',
598 help =
'Do not print warnings about missing luminosity information')
599 inputGroup.add_option (
'--runEventLumi', dest=
'relOrder',
601 help =
'Parse event list assuming Run, Event #, Lumi# order')
602 inputGroup.add_option (
'--weights', dest=
'weights', action=
'store_true',
603 help =
'Read fourth column as a weight')
604 modeGroup.add_option (
'--print', dest=
'printValues', action=
'store_true',
605 help =
'Print X and Y values of EDF plot')
606 modeGroup.add_option (
'--runsWithLumis', dest=
'runsWithLumis',
607 type=
'string',action=
'append', default=[],
608 help=
'Print out run and lumi sections corresponding to integrated luminosities provided and then exits')
609 modeGroup.add_option (
'--edfMode', dest=
'edfMode', type=
'string',
611 help=
"EDF Mode %s (default '%%default')" % allowedEDF)
612 parser.add_option_group (plotGroup)
613 parser.add_option_group (rangeGroup)
614 parser.add_option_group (inputGroup)
615 parser.add_option_group (modeGroup)
616 (options, args) = parser.parse_args()
618 if options.edfMode
not in allowedEDF:
619 raise RuntimeError(
"edfMode (currently '%s') must be one of %s" \
620 % (options.edfMode, allowedEDF))
622 if len (args) != 3
and not (options.runsWithLumis
and len(args) >= 1):
623 raise RuntimeError(
"Must provide lumi.csv, events.txt, and output.png")
629 cont = LumiInfoCont (args[0], **options.__dict__)
630 cont.minRun = options.minRun
631 cont.maxRun = options.maxRun
632 cont.minIntLum = options.minIntLum
633 cont.maxIntLum = options.maxIntLum
639 if options.runsWithLumis:
641 for line
in options.runsWithLumis:
642 pieces = sepRE.split (line)
645 recLumValue = float (piece)
647 raise RuntimeError(
"'%s' in '%s' is not a float" % \
650 raise RuntimeError(
"You must provide positive values for -runsWithLumis ('%f' given)" % recLumValue)
651 recLumis.append (recLumValue)
653 raise RuntimeError(
"What did Charles do now?")
656 recLumValue = recLumis [recLumIndex]
659 for key, lumi
in six.iteritems(cont):
660 if prevRecLumi >= recLumValue
and recLumValue < lumi.totalRecorded:
662 print(
"%s contains total recorded lumi %f" % \
663 (key.__str__(), recLumValue))
666 if recLumIndex == len (recLumis):
669 recLumValue = recLumis [recLumIndex]
670 if prevRecLumi >= recLumValue
and recLumValue < lumi.totalRecorded:
672 print(
"%s contains total recorded lumi %f" % \
673 (key.__str__(), recLumValue))
678 prevRecLumi = lumi.totalRecorded
679 if recLumIndex < len (recLumis):
680 print(
"Theses lumis not found: %s" % recLumis[recLumIndex:])
686 if options.edfMode !=
'time' and not cont.xingInfo:
687 raise RuntimeError(
"'%s' does not have Xing info" % args[0])
688 eventsDict, totalWeight = loadEvents (args[1], cont, options)
689 makeEDFplot (cont, eventsDict, totalWeight, args[2], options)