CMS 3D CMS Logo

generateEDF.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 from builtins import zip
4 from builtins import object
5 from past.utils import old_div
6 from builtins import range
7 import sys
8 import re
9 from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
10 from pprint import pprint
11 import array
12 import ROOT
13 import math
14 
15 sepRE = re.compile (r'[\s,;:]+')
16 nonSpaceRE = re.compile (r'\S')
17 
18 
23 
24 class LumiInfo (object):
25 
26  lastSingleXingRun = 136175
27  lumiSectionLength = 23.310779
28 
29  def __init__ (self, line):
30  self.totInstLum = 0.
31  self.aveInstLum = 0.
32  self.numXings = 0
33  self.instLums = []
34  self.events = []
35  self.xingInfo = False
36  self.badXingInfo = False
37  pieces = sepRE.split (line.strip())
38  size = len (pieces)
39  if size % 2:
40  raise RuntimeError("Odd number of pieces")
41  if size < 4:
42  raise RuntimeError("Not enough pieces")
43  try:
44  self.run = int (pieces[0])
45  self.lumi = int (pieces[1])
46  self.delivered = float (pieces[2])
47  self.recorded = float (pieces[3])
48  except:
49  raise RuntimeError("Pieces not right format")
50  if size > 4:
51  try:
52  for xing, lum in zip (pieces[4::2],pieces[5::2]):
53  xing = int (xing)
54  lum = float (lum)
55  self.instLums.append( (xing, lum) )
56  self.totInstLum += lum
57  self.numXings += 1
58  except:
59  raise RuntimeError("Inst Lumi Info malformed")
60  self.aveInstLum = old_div(self.totInstLum, (self.numXings))
61  self.xingInfo = True
62  self.key = (self.run, self.lumi)
63  self.keyString = self.key.__str__()
64 
65 
66  def fixXingInfo (self):
67  if self.numXings:
68  # You shouldn't try and fix an event if it already has
69  # xing information.
70  raise RuntimeError("This event %s already has Xing information" \
71  % self.keyString)
72  if self.run > LumiInfo.lastSingleXingRun:
73  # this run may have more than one crossing. I don't know
74  # how to fix this.
75  self.badXingInfo = True
76  return False
77  self.numXings = 1
78  xing = 1
79  self.aveInstLum = self.totInstLum = lum = \
80  old_div(self.delivered, LumiInfo.lumiSectionLength)
81  self.instLums.append( (xing, lum) )
82  self.xingInfo = True
83  return True
84 
85 
86  def deadtime (self):
87  if not self.delivered:
88  return 1
89  return 1 - (old_div(self.recorded, self.delivered))
90 
91 
92  def __str__ (self):
93  return "%6d, %4d: %6.1f (%4.1f%%) %4.2f (%3d)" % \
94  (self.run,
95  self.lumi,
96  self.delivered,
97  self.deadtime(),
98  self.totInstLum,
99  self.numXings)
100 
101 
102 
107 
108 class LumiInfoCont (dict):
109 
110  def __init__ (self, filename, **kwargs):
111  print("loading luminosity information from '%s'." % filename)
112  source = open (filename, 'r')
113  self.minMaxKeys = ['totInstLum', 'aveInstLum', 'numXings',
114  'delivered', 'recorded']
115  self._min = {}
116  self._max = {}
117  self.totalRecLum = 0.
118  self.xingInfo = False
119  self.allowNoXing = kwargs.get ('ignore')
120  self.noWarnings = kwargs.get ('noWarnings')
121  self.minRun = 0
122  self.maxRun = 0
123  self.minIntLum = 0
124  self.maxIntLum = 0
125 
126  for key in self.minMaxKeys:
127  self._min[key] = -1
128  self._max[key] = 0
129  for line in source:
130  try:
131  lumi = LumiInfo (line)
132  except:
133  continue
134  self[lumi.key] = lumi
135  self.totalRecLum += lumi.recorded
136  if not self.xingInfo and lumi.xingInfo:
137  self.xingInfo = True
138  if lumi.xingInfo:
139  #print "yes", lumi.keyString
140  if not self.xingInfo:
141  print("huh?")
142  for key in self.minMaxKeys:
143  val = getattr (lumi, key)
144  if val < self._min[key] or self._min[key] < 0:
145  self._min[key] = val
146  if val > self._max[key] or not self._max[key]:
147  self._max[key] = val
148  source.close()
149 
154  self.invunits = 'nb'
155  lumFactor = 1e3
156  if self.totalRecLum > 1e9:
157  lumFactor = 1e9
158  self.invunits = 'fb'
159  elif self.totalRecLum > 1e6:
160  lumFactor = 1e6
161  self.invunits = 'pb'
162  # use lumFactor to make everything consistent
163  #print "units", self.invunits, "factor", lumFactor
164  self.totalRecLum /= lumFactor
165  for lumis in self.values():
166  lumis.delivered /= lumFactor
167  lumis.recorded /= lumFactor
168  # Probably want to rename this next subroutine, but I'll leave
169  # it alone for now...
170  self._integrateContainer()
171 
172 
173 
174  def __str__ (self):
175  retval = 'run, lum del ( dt ) inst (#xng)\n'
176  for key, value in sorted (self.items()):
177  retval += "%s\n" % value
178  return retval
179 
180 
181  def min (self, key):
182  return self._min[key]
183 
184 
185  def max (self, key):
186  return self._max[key]
187 
188 
189  def keys (self):
190  return sorted (dict.keys (self))
191 
192 
193  def iteritems (self):
194  return sorted (dict.iteritems (self))
195 
196 
198  # calculate numbers for recorded integrated luminosity
199  total = 0.
200  for key, lumi in self.items():
201  total += lumi.recorded
202  lumi.totalRecorded = total
203  lumi.fracRecorded = old_div(total, self.totalRecLum)
204  # calculate numbers for average xing instantaneous luminosity
205  if not self.xingInfo:
206  # nothing to do here
207  return
208  xingKeyList = []
209  maxAveInstLum = 0.
210  for key, lumi in self.items():
211  if not lumi.xingInfo and not lumi.fixXingInfo():
212  if not self.noWarnings:
213  print("Do not have lumi xing info for %s" % lumi.keyString)
214  if not self.allowNoXing:
215  print("Setting no Xing info flag")
216  self.xingInfo = False
217  return
218  continue
219  xingKeyList.append( (lumi.aveInstLum, key) )
220  if lumi.aveInstLum > maxAveInstLum:
221  maxAveInstLum = lumi.aveInstLum
222  xingKeyList.sort()
223  total = 0.
224  for tup in xingKeyList:
225  lumi = self[tup[1]]
226  total += lumi.recorded
227  lumi.totalAXILrecorded = total
228  lumi.fracAXILrecorded = old_div(total, self.totalRecLum)
229  lumi.fracAXIL = old_div(lumi.aveInstLum, maxAveInstLum)
230 
231 
232 
237 
238 def loadEvents (filename, cont, options):
239  eventsDict = {}
240  print("loading events from '%s'" % filename)
241  events = open (filename, 'r')
242  runIndex, lumiIndex, eventIndex, weightIndex = 0, 1, 2, 3
243  if options.relOrder:
244  lumiIndex, eventIndex = 2,1
245  minPieces = 3
246  totalWeight = 0.
247  if options.weights:
248  minPieces = 4
249  for line in events:
250  pieces = sepRE.split (line.strip())
251  if len (pieces) < minPieces:
252  if nonSpaceRE.search (line):
253  print("skipping", line)
254  continue
255  try:
256  run, lumi, event = int( pieces[runIndex] ), \
257  int( pieces[lumiIndex] ), \
258  int( pieces[eventIndex] )
259  except:
260  continue
261  key = (run, lumi)
262  if key not in cont:
263  if options.ignore:
264  print("Warning, %s is not found in the lumi information" \
265  % key.__str__())
266  continue
267  else:
268  raise RuntimeError("%s is not found in lumi information. Use '--ignoreNoLumiEvents' option to ignore these events and continue." \
269  % key.__str__())
270  if options.edfMode != 'time' and not cont[key].xingInfo:
271  if options.ignore:
272  print("Warning, %s does not have Xing information" \
273  % key.__str__())
274  continue
275  else:
276  raise RuntimeError("%s does not have Xing information. Use '--ignoreNoLumiEvents' option to ignore these events and continue." \
277  % key.__str__())
278  if options.weights:
279  weight = float (pieces[weightIndex])
280  else:
281  weight = 1
282  eventsDict.setdefault( key, []).append( (event, weight) )
283  totalWeight += weight
284  events.close()
285  return eventsDict, totalWeight
286 
287 
288 def makeEDFplot (lumiCont, eventsDict, totalWeight, outputFile, options):
289  # make TGraph
290  xVals = [0]
291  yVals = [0]
292  expectedVals = [0]
293  predVals = [0]
294  weight = 0
295  expectedChunks = []
296 
299  if 'time' == options.edfMode:
300  # if we have a minimum run number, clear the lists
301  if lumiCont.minRun or lumiCont.minIntLum:
302  xVals = []
303  yVals = []
304  expectedVals = []
305  predVals = []
306  # loop over events
307  for key, eventList in sorted( eventsDict.items() ):
308  usePoints = True
309  # should we add this point?
310  if lumiCont.minRun and lumiCont.minRun > key[0] or \
311  lumiCont.maxRun and lumiCont.maxRun < key[0]:
312  usePoints = False
313  for event in eventList:
314  weight += event[1]
315  if not usePoints:
316  continue
317  factor = old_div(weight, totalWeight)
318  try:
319  intLum = lumiCont[key].totalRecorded
320  except:
321  raise RuntimeError("key %s not found in lumi information" \
322  % key.__str__())
323  if lumiCont.minIntLum and lumiCont.minIntLum > intLum or \
324  lumiCont.maxIntLum and lumiCont.maxIntLum < intLum:
325  continue
326  lumFrac = old_div(intLum, lumiCont.totalRecLum)
327  xVals.append( lumiCont[key].totalRecorded)
328  yVals.append (factor)
329  expectedVals.append (lumFrac)
330  predVals.append (lumFrac * options.pred)
331  # put on the last point if we aren't giving a maximum run
332  if not lumiCont.maxRun and not lumiCont.maxIntLum:
333  xVals.append (lumiCont.totalRecLum)
334  yVals.append (1)
335  expectedVals.append (1)
336  predVals.append (options.pred)
337 
340  if options.resetExpected:
341  slope = old_div((yVals[-1] - yVals[0]), (xVals[-1] - xVals[0]))
342  print("slope", slope)
343  for index, old in enumerate (expectedVals):
344  expectedVals[index] = yVals[0] + \
345  slope * (xVals[index] - xVals[0])
346 
349  if options.breakExpectedIntLum:
350  breakExpectedIntLum = []
351  for chunk in options.breakExpectedIntLum:
352  pieces = sepRE.split (chunk)
353  try:
354  for piece in pieces:
355  breakExpectedIntLum.append( float(piece) )
356  except:
357  raise RuntimeError("'%s' from '%s' is not a valid float" \
358  % (piece, chunk))
359  breakExpectedIntLum.sort()
360  boundaries = []
361  breakIndex = 0
362  done = False
363  for index, xPos in enumerate (xVals):
364  if xPos > breakExpectedIntLum[breakIndex]:
365  boundaries.append (index)
366  while breakIndex < len (breakExpectedIntLum):
367  breakIndex += 1
368  if breakIndex >= len (breakExpectedIntLum):
369  done = True
370  break
371  # If this next position is different, than
372  # we're golden. Otherwise, let it go through
373  # the loop again.
374  if xPos <= breakExpectedIntLum[breakIndex]:
375  break
376  if done:
377  break
378  # do we have any boundaries?
379  if not boundaries:
380  raise RuntimeError("No values of 'breakExpectedIntLum' are in current range.")
381  # is the first boundary at 0? If not, add 0
382  if boundaries[0]:
383  boundaries.insert (0, 0)
384  # is the last boundary at the end? If not, make the end a
385  # boundary
386  if boundaries[-1] != len (xVals) - 1:
387  boundaries.append( len (xVals) - 1 )
388  rangeList = list(zip (boundaries, boundaries[1:]))
389  for thisRange in rangeList:
390  upper = thisRange[1]
391  lower = thisRange[0]
392  slope = old_div((yVals[upper] - yVals[lower]), \
393  (xVals[upper] - xVals[lower]))
394  print("slope", slope)
395  # now go over the range inclusively
396  pairList = []
397  for index in range (lower, upper + 1):
398  newExpected = yVals[lower] + \
399  slope * (xVals[index] - xVals[lower])
400  pairList.append( (xVals[index], newExpected) )
401  expectedVals[index] = newExpected
402  expectedChunks.append (pairList)
403 
406  elif 'instLum' == options.edfMode or 'instIntLum' == options.edfMode:
407  eventTupList = []
408  if not lumiCont.xingInfo:
409  raise RuntimeError("Luminosity Xing information missing.")
410  for key, eventList in sorted( eventsDict.items() ):
411  try:
412  lumi = lumiCont[key]
413  instLum = lumi.aveInstLum
414  fracAXIL = lumi.fracAXILrecorded
415  totalAXIL = lumi.totalAXILrecorded
416  except:
417  raise RuntimeError("key %s not found in lumi information" \
418  % key.__str__())
419  for event in eventList:
420  eventTupList.append( (instLum, fracAXIL, totalAXIL, key,
421  event[0], event[1], ) )
422  eventTupList.sort()
423  for eventTup in eventTupList:
424  weight += eventTup[5]
425  factor = old_div(weight, totalWeight)
426  if 'instLum' == options.edfMode:
427  xVals.append (eventTup[0])
428  else:
429  xVals.append (eventTup[2])
430  yVals.append (factor)
431  expectedVals.append (eventTup[1])
432  predVals.append (eventTup[1] * options.pred)
433  else:
434  raise RuntimeError("It looks like Charles screwed up if you are seeing this.")
435 
436  size = len (xVals)
437  step = int (old_div(math.sqrt(size), 2) + 1)
438  if options.printValues:
439  for index in range (size):
440  print("%8f %8f %8f" % (xVals[index], yVals[index], expectedVals[index]), end=' ')
441  if index > step:
442  denom = xVals[index] - xVals[index - step]
443  numer = yVals[index] - yVals[index - step]
444  if denom:
445  print(" %8f" % (old_div(numer, denom)), end=' ')
446  if 0 == index % step:
447  print(" **", end=' ')
449  print()
450  print()
451 
452  xArray = array.array ('d', xVals)
453  yArray = array.array ('d', yVals)
454  expected = array.array ('d', expectedVals)
455  graph = ROOT.TGraph( size, xArray, yArray)
456  graph.SetTitle (options.title)
457  graph.SetMarkerStyle (20)
458  expectedGraph = ROOT.TGraph( size, xArray, expected)
459  expectedGraph.SetLineColor (ROOT.kRed)
460  expectedGraph.SetLineWidth (3)
461  if options.noDataPoints:
462  expectedGraph.SetLineStyle (2)
463 
464  # run statistical tests
465  if options.weights:
466  print("average weight per event:", old_div(weight, ( size - 1)))
467  maxDistance = ROOT.TMath.KolmogorovTest (size, yArray,
468  size, expected,
469  "M")
470  prob = ROOT.TMath.KolmogorovProb( maxDistance * math.sqrt( size ) )
471 
472  # display everything
473  ROOT.gROOT.SetStyle('Plain')
474  ROOT.gROOT.SetBatch()
475  c1 = ROOT.TCanvas()
476  graph.GetXaxis().SetRangeUser (min (xVals), max (xVals))
477  minValue = min (min(yVals), min(expected))
478  if options.pred:
479  minValue = min (minValue, min (predVals))
480  graph.GetYaxis().SetRangeUser (minValue,
481  max (max(yVals), max(expected), max(predVals)))
482  graph.SetLineWidth (3)
483  if options.noDataPoints:
484  graph.Draw ("AL")
485  else:
486  graph.Draw ("ALP")
487  if 'instLum' == options.edfMode:
488  graph.GetXaxis().SetTitle ("Average Xing Inst. Luminosity (1/ub/s)")
489  graph.GetXaxis().SetRangeUser (0., lumiCont.max('aveInstLum'))
490  else:
491  if 'instIntLum' == options.edfMode:
492  graph.GetXaxis().SetTitle ("Integrated Luminosity - Inst. Lum. Ordered (1/%s)" \
493  % lumiCont.invunits)
494  else:
495  graph.GetXaxis().SetTitle ("Integrated Luminosity (1/%s)" \
496  % lumiCont.invunits)
497  graph.GetYaxis().SetTitle ("Fraction of Events Seen")
498  expectedGraphs = []
499  if expectedChunks:
500  for index, chunk in enumerate (expectedChunks):
501  expectedXarray = array.array ('d', [item[0] for item in chunk])
502  expectedYarray = array.array ('d', [item[1] for item in chunk])
503  expectedGraph = ROOT.TGraph( len(chunk),
504  expectedXarray,
505  expectedYarray )
506  expectedGraph.SetLineWidth (3)
507  if options.noDataPoints:
508  expectedGraph.SetLineStyle (2)
509  if index % 2:
510  expectedGraph.SetLineColor (ROOT.kBlue)
511  else:
512  expectedGraph.SetLineColor (ROOT.kRed)
513  expectedGraph.Draw("L")
514  expectedGraphs.append (expectedGraph)
515  exptectedGraph = expectedGraphs[0]
516  else:
517  expectedGraph.Draw ("L")
518  green = 0
519  if options.pred:
520  predArray = array.array ('d', predVals)
521  green = ROOT.TGraph (size, xArray, predArray)
522  green.SetLineWidth (3)
523  green.SetLineColor (8)
524  green.Draw ('l')
525  legend = ROOT.TLegend(0.15, 0.65, 0.50, 0.85)
526  legend.SetFillStyle (0)
527  legend.SetLineColor(ROOT.kWhite)
528  observed = 'Observed'
529  if options.weights:
530  observed += ' (weighted)'
531  legend.AddEntry(graph, observed,"PL")
532  if options.resetExpected:
533  legend.AddEntry(expectedGraph, "Expected from partial yield","L")
534  else:
535  legend.AddEntry(expectedGraph, "Expected from total yield","L")
536  if options.pred:
537  legend.AddEntry(green, options.predLabel,"L")
538  legend.AddEntry("","D_{stat}=%.3f, N=%d" % (maxDistance, size),"")
539  legend.AddEntry("","P_{KS}=%.3f" % prob,"")
540  legend.Draw()
541 
542  # save file
543  c1.Print (outputFile)
544 
545 
546 
553 
554 if __name__ == '__main__':
555 
558  allowedEDF = ['time', 'instLum', 'instIntLum']
559  parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter, usage='%(prog)s [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.')
560  plotGroup = parser.add_argument_group("Plot Options")
561  rangeGroup = parser.add_argument_group("Range Options")
562  inputGroup = parser.add_argument_group("Input Options")
563  modeGroup = parser.add_argument_group("Mode Options")
564  plotGroup.add_argument('--title', dest='title', type=str,
565  default = 'Empirical Distribution Function',
566  help = 'title of plot')
567  plotGroup.add_argument('--predicted', dest='pred', type=float,
568  default = 0,
569  help = 'factor by which predicted curve is greater than observed')
570  plotGroup.add_argument('--predLabel', dest='predLabel', type=str,
571  default = 'Predicted',
572  help = 'label of predicted in legend')
573  plotGroup.add_argument('--noDataPoints', dest='noDataPoints',
574  default = False, action='store_true',
575  help="Draw lines but no points for data")
576  rangeGroup.add_argument('--minRun', dest='minRun', type=int, default=0,
577  help='Minimum run number to consider')
578  rangeGroup.add_argument('--maxRun', dest='maxRun', type=int, default=0,
579  help='Maximum run number to consider')
580  rangeGroup.add_argument('--minIntLum', dest='minIntLum', type=float, default=0,
581  help='Minimum integrated luminosity to consider')
582  rangeGroup.add_argument('--maxIntLum', dest='maxIntLum', type=float, default=0,
583  help='Maximum integrated luminosity to consider')
584  rangeGroup.add_argument('--resetExpected', dest='resetExpected',
585  default = False, action='store_true',
586  help='Reset expected from total yield to highest point considered')
587  rangeGroup.add_argument('--breakExpectedIntLum', dest='breakExpectedIntLum',
588  type=str, action='append', default=[],
589  help='Break expected curve into pieces at integrated luminosity boundaries')
590  inputGroup.add_argument('--ignoreNoLumiEvents', dest='ignore',
591  default = False, action='store_true',
592  help = 'Ignore (with a warning) events that do not have a lumi section')
593  inputGroup.add_argument('--noWarnings', dest='noWarnings',
594  default = False,action='store_true',
595  help = 'Do not print warnings about missing luminosity information')
596  inputGroup.add_argument('--runEventLumi', dest='relOrder',
597  default = False, action='store_true',
598  help = 'Parse event list assuming Run, Event #, Lumi# order')
599  inputGroup.add_argument('--weights', dest='weights', default = False, action='store_true',
600  help = 'Read fourth column as a weight')
601  modeGroup.add_argument('--print', dest='printValues', default = False, action='store_true',
602  help = 'Print X and Y values of EDF plot')
603  modeGroup.add_argument('--runsWithLumis', dest='runsWithLumis',
604  type=str,action='append', default=[],
605  help='Print out run and lumi sections corresponding to integrated luminosities provided and then exits')
606  modeGroup.add_argument('--edfMode', dest='edfMode', type=str,
607  default='time',
608  help="EDF Mode", choices=allowedEDF)
609  parser.add_argument("lumi_csv", metavar="lumi.csv", type=str)
610  parser.add_argument("events_txt", metavar="events.txt", type=str, nargs='?')
611  parser.add_argument("output_png", metavar="output.png", type=str, nargs='?')
612  options = parser.parse_args()
613 
614  if not options.runsWithLumis and (options.events_txt is None or options.output_png is None):
615  parser.error("Must provide lumi.csv, events.txt, and output.png")
616 
617 
620  cont = LumiInfoCont (options.lumi_csv, **options.__dict__)
621  cont.minRun = options.minRun
622  cont.maxRun = options.maxRun
623  cont.minIntLum = options.minIntLum
624  cont.maxIntLum = options.maxIntLum
625 
626 
630  if options.runsWithLumis:
631  recLumis = []
632  for line in options.runsWithLumis:
633  pieces = sepRE.split (line)
634  for piece in pieces:
635  try:
636  recLumValue = float (piece)
637  except:
638  raise RuntimeError("'%s' in '%s' is not a float" % \
639  (piece, line))
640  if recLumValue <= 0:
641  raise RuntimeError("You must provide positive values for -runsWithLumis ('%f' given)" % recLumValue)
642  recLumis.append (recLumValue)
643  if not recLumis:
644  raise RuntimeError("What did Charles do now?")
645  recLumis.sort()
646  recLumIndex = 0
647  recLumValue = recLumis [recLumIndex]
648  prevRecLumi = 0.
649  done = False
650  for key, lumi in cont.items():
651  if prevRecLumi >= recLumValue and recLumValue < lumi.totalRecorded:
652  # found it
653  print("%s contains total recorded lumi %f" % \
654  (key.__str__(), recLumValue))
655  while True:
656  recLumIndex += 1
657  if recLumIndex == len (recLumis):
658  done = True
659  break
660  recLumValue = recLumis [recLumIndex]
661  if prevRecLumi >= recLumValue and recLumValue < lumi.totalRecorded:
662  # found it
663  print("%s contains total recorded lumi %f" % \
664  (key.__str__(), recLumValue))
665  else:
666  break
667  if done:
668  break
669  prevRecLumi = lumi.totalRecorded
670  if recLumIndex < len (recLumis):
671  print("Theses lumis not found: %s" % recLumis[recLumIndex:])
672  sys.exit()
673 
674 
677  if options.edfMode != 'time' and not cont.xingInfo:
678  raise RuntimeError("'%s' does not have Xing info" % options.lumi_csv)
679  eventsDict, totalWeight = loadEvents (options.events_txt, cont, options)
680  makeEDFplot (cont, eventsDict, totalWeight, options.output_png, options)
def __init__(self, line)
Definition: generateEDF.py:29
def __init__(self, filename, kwargs)
Definition: generateEDF.py:110
def loadEvents(filename, cont, options)
General Functions
Definition: generateEDF.py:238
invunits
Now that everything is setup, switch integrated ## luminosity to more reasonable units.
Definition: generateEDF.py:154
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def makeEDFplot(lumiCont, eventsDict, totalWeight, outputFile, options)
Definition: generateEDF.py:288
def fixXingInfo(self)
Definition: generateEDF.py:66
LumiInfoCont Class
Definition: generateEDF.py:108
LumiInfo Class
Definition: generateEDF.py:24