test
CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
utilities.py
Go to the documentation of this file.
1 """
2 Utilities for rootplot including histogram classes.
3 """
4 
5 __license__ = '''\
6 Copyright (c) 2009-2010 Jeff Klukas <klukas@wisc.edu>
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 THE SOFTWARE.
25 '''
26 
27 ################ Import python libraries
28 
29 import math
30 import ROOT
31 import re
32 import copy
33 import array
34 import os.path
35 import sys
36 import fnmatch
37 from random import gauss
38 
39 ################ Define classes
40 
41 class Hist2D(object):
42  """A container to hold the parameters from a 2D ROOT histogram."""
43  def __init__(self, hist, label="__nolabel__", title=None,
44  xlabel=None, ylabel=None):
45  try:
46  if not hist.InheritsFrom("TH2"):
47  raise TypeError("%s does not inherit from TH2" % hist)
48  except:
49  raise TypeError("%s is not a ROOT object" % hist)
50  self.rootclass = hist.ClassName()
51  self.name = hist.GetName()
52  self.nbinsx = nx = hist.GetNbinsX()
53  self.nbinsy = ny = hist.GetNbinsY()
54  self.binlabelsx = process_bin_labels([hist.GetXaxis().GetBinLabel(i)
55  for i in range(1, nx + 1)])
56  if self.binlabelsx:
57  self.nbinsx = nx = self.binlabelsx.index('')
58  self.binlabelsx = self.binlabelsx[:ny]
59  self.binlabelsy = process_bin_labels([hist.GetYaxis().GetBinLabel(i)
60  for i in range(1, ny + 1)])
61  if self.binlabelsy:
62  self.nbinsy = ny = self.binlabelsy.index('')
63  self.binlabelsy = self.binlabelsy[:ny]
64  self.entries = hist.GetEntries()
65  self.content = [[hist.GetBinContent(i, j) for i in range(1, nx + 1)]
66  for j in range(1, ny + 1)]
67  self.xedges = [hist.GetXaxis().GetBinLowEdge(i)
68  for i in range(1, nx + 2)]
69  self.yedges = [hist.GetYaxis().GetBinLowEdge(i)
70  for i in range(1, ny + 2)]
71  self.x = [(self.xedges[i+1] + self.xedges[i])/2
72  for i in range(nx)]
73  self.y = [(self.yedges[i+1] + self.yedges[i])/2
74  for i in range(ny)]
75  self.title = title or hist.GetTitle()
76  self.xlabel = xlabel or hist.GetXaxis().GetTitle()
77  self.ylabel = ylabel or hist.GetYaxis().GetTitle()
78  self.label = label
79  def _flat_content(self):
80  flatcontent = []
81  for row in self.content:
82  flatcontent += row
83  return flatcontent
84  def __getitem__(self, index):
85  """Return contents of indexth bin: x.__getitem__(y) <==> x[y]"""
86  return self._flat_content()[index]
87  def __len__(self):
88  """Return the number of bins: x.__len__() <==> len(x)"""
89  return len(self._flat_content())
90  def __iter__(self):
91  """Iterate through bins: x.__iter__() <==> iter(x)"""
92  return iter(self._flat_content())
93  def TH2F(self, name=""):
94  """Return a ROOT.TH2F object with contents of this Hist2D."""
95  th2f = ROOT.TH2F(name, "",
96  self.nbinsx, array.array('f', self.xedges),
97  self.nbinsy, array.array('f', self.yedges))
98  th2f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
99  for ix in range(self.nbinsx):
100  for iy in range(self.nbinsy):
101  th2f.SetBinContent(ix + 1, iy + 1, self.content[iy][ix])
102  return th2f
103 
104 class Hist(object):
105  """A container to hold the parameters from a ROOT histogram."""
106  def __init__(self, hist, label="__nolabel__",
107  name=None, title=None, xlabel=None, ylabel=None):
108  try:
109  hist.GetNbinsX()
110  self.__init_TH1(hist)
111  except AttributeError:
112  try:
113  hist.GetN()
114  self.__init_TGraph(hist)
115  except AttributeError:
116  raise TypeError("%s is not a 1D histogram or TGraph" % hist)
117  self.rootclass = hist.ClassName()
118  self.name = name or hist.GetName()
119  self.title = title or hist.GetTitle().split(';')[0]
120  self.xlabel = xlabel or hist.GetXaxis().GetTitle()
121  self.ylabel = ylabel or hist.GetYaxis().GetTitle()
122  self.label = label
123  def __init_TH1(self, hist):
124  self.nbins = n = hist.GetNbinsX()
125  self.binlabels = process_bin_labels([hist.GetXaxis().GetBinLabel(i)
126  for i in range(1, n + 1)])
127  if self.binlabels and '' in self.binlabels:
128  # Get rid of extra non-labeled bins
129  self.nbins = n = self.binlabels.index('')
130  self.binlabels = self.binlabels[:n]
131  self.entries = hist.GetEntries()
132  self.xedges = [hist.GetBinLowEdge(i) for i in range(1, n + 2)]
133  self.x = [(self.xedges[i+1] + self.xedges[i])/2 for i in range(n)]
134  self.xerr = [(self.xedges[i+1] - self.xedges[i])/2 for i in range(n)]
135  self.xerr = [self.xerr[:], self.xerr[:]]
136  self.width = [(self.xedges[i+1] - self.xedges[i]) for i in range(n)]
137  self.y = [hist.GetBinContent(i) for i in range(1, n + 1)]
138  self.yerr = [hist.GetBinError( i) for i in range(1, n + 1)]
139  self.yerr = [self.yerr[:], self.yerr[:]]
140  self.underflow = hist.GetBinContent(0)
141  self.overflow = hist.GetBinContent(self.nbins + 1)
142  def __init_TGraph(self, hist):
143  self.nbins = n = hist.GetN()
144  self.x, self.y = [], []
145  x, y = ROOT.Double(0), ROOT.Double(0)
146  for i in range(n):
147  hist.GetPoint(i, x, y)
148  self.x.append(copy.copy(x))
149  self.y.append(copy.copy(y))
150  lower = [max(0, hist.GetErrorXlow(i)) for i in xrange(n)]
151  upper = [max(0, hist.GetErrorXhigh(i)) for i in xrange(n)]
152  self.xerr = [lower[:], upper[:]]
153  lower = [max(0, hist.GetErrorYlow(i)) for i in xrange(n)]
154  upper = [max(0, hist.GetErrorYhigh(i)) for i in xrange(n)]
155  self.yerr = [lower[:], upper[:]]
156  self.xedges = [self.x[i] - self.xerr[0][i] for i in xrange(n)]
157  self.xedges.append(self.x[n - 1] + self.xerr[1][n - 1])
158  self.width = [self.xedges[i + 1] - self.xedges[i] for i in range(n)]
159  self.underflow, self.overflow = 0, 0
160  self.binlabels = None
161  self.entries = n
162  def __add__(self, b):
163  """Return the sum of self and b: x.__add__(y) <==> x + y"""
164  c = copy.copy(self)
165  for i in range(len(self)):
166  c.y[i] += b.y[i]
167  c.yerr[0][i] += b.yerr[0][i]
168  c.yerr[1][i] += b.yerr[1][i]
169  c.overflow += b.overflow
170  c.underflow += b.underflow
171  return c
172  def __sub__(self, b):
173  """Return the difference of self and b: x.__sub__(y) <==> x - y"""
174  c = copy.copy(self)
175  for i in range(len(self)):
176  c.y[i] -= b.y[i]
177  c.yerr[0][i] -= b.yerr[0][i]
178  c.yerr[1][i] -= b.yerr[1][i]
179  c.overflow -= b.overflow
180  c.underflow -= b.underflow
181  return c
182  def __div__(self, denominator):
183  return self.divide(denominator)
184  def __getitem__(self, index):
185  """Return contents of indexth bin: x.__getitem__(y) <==> x[y]"""
186  return self.y[index]
187  def __setitem__(self, index, value):
188  """Set contents of indexth bin: x.__setitem__(i, y) <==> x[i]=y"""
189  self.y[index] = value
190  def __len__(self):
191  """Return the number of bins: x.__len__() <==> len(x)"""
192  return self.nbins
193  def __iter__(self):
194  """Iterate through bins: x.__iter__() <==> iter(x)"""
195  return iter(self.y)
196  def min(self, threshold=None):
197  """Return the y-value of the bottom tip of the lowest errorbar."""
198  vals = [(yval - yerr) for yval, yerr in zip(self.y, self.yerr[0])
199  if (yval - yerr) > threshold]
200  if vals:
201  return min(vals)
202  else:
203  return threshold
204  def av_xerr(self):
205  """Return average between the upper and lower xerr."""
206  return [(self.xerr[0][i] + self.xerr[1][i]) / 2
207  for i in range(self.nbins)]
208  def av_yerr(self):
209  """Return average between the upper and lower yerr."""
210  return [(self.yerr[0][i] + self.yerr[1][i]) / 2
211  for i in range(self.nbins)]
212  def scale(self, factor):
213  """
214  Scale contents, errors, and over/underflow by the given scale factor.
215  """
216  self.y = [x * factor for x in self.y]
217  self.yerr[0] = [x * factor for x in self.yerr[0]]
218  self.yerr[1] = [x * factor for x in self.yerr[1]]
219  self.overflow *= factor
220  self.underflow *= factor
221  def delete_bin(self, index):
222  """
223  Delete a the contents of a bin, sliding all the other data one bin to
224  the left. This can be useful for histograms with labeled bins.
225  """
226  self.nbins -= 1
227  self.xedges.pop()
228  self.x.pop()
229  self.width.pop()
230  self.y.pop(index)
231  self.xerr[0].pop(index)
232  self.xerr[1].pop(index)
233  self.yerr[0].pop(index)
234  self.yerr[1].pop(index)
235  if self.binlabels:
236  self.binlabels.pop(index)
237  def TH1F(self, name=None):
238  """Return a ROOT.TH1F object with contents of this Hist"""
239  th1f = ROOT.TH1F(name or self.name, "", self.nbins,
240  array.array('f', self.xedges))
241  th1f.Sumw2()
242  th1f.SetTitle("%s;%s;%s" % (self.title, self.xlabel, self.ylabel))
243  for i in range(self.nbins):
244  th1f.SetBinContent(i + 1, self.y[i])
245  try:
246  th1f.SetBinError(i + 1, (self.yerr[0][i] + self.yerr[1][i]) / 2)
247  except TypeError:
248  th1f.SetBinError(i + 1, self.yerr[i])
249  if self.binlabels:
250  th1f.GetXaxis().SetBinLabel(i + 1, self.binlabels[i])
251  th1f.SetBinContent(0, self.underflow)
252  th1f.SetBinContent(self.nbins + 2, self.overflow)
253  th1f.SetEntries(self.entries)
254  return th1f
255  def TGraph(self, name=None):
256  """Return a ROOT.TGraphAsymmErrors object with contents of this Hist"""
257  x = array.array('f', self.x)
258  y = array.array('f', self.y)
259  xl = array.array('f', self.xerr[0])
260  xh = array.array('f', self.xerr[1])
261  yl = array.array('f', self.yerr[0])
262  yh = array.array('f', self.yerr[1])
263  tgraph = ROOT.TGraphAsymmErrors(self.nbins, x, y, xl, xh, yl, yh)
264  tgraph.SetName(name or self.name)
265  tgraph.SetTitle('%s;%s;%s' % (self.title, self.xlabel, self.ylabel))
266  return tgraph
267  def divide(self, denominator):
268  """
269  Return the simple quotient with errors added in quadrature.
270 
271  This function is called by the division operator:
272  hist3 = hist1.divide_wilson(hist2) <--> hist3 = hist1 / hist2
273  """
274  if len(self) != len(denominator):
275  raise TypeError("Cannot divide %s with %i bins by "
276  "%s with %i bins." %
277  (denominator.name, len(denominator),
278  self.name, len(self)))
279  quotient = copy.deepcopy(self)
280  num_yerr = self.av_yerr()
281  den_yerr = denominator.av_yerr()
282  quotient.yerr = [0. for i in range(self.nbins)]
283  for i in range(self.nbins):
284  if denominator[i] == 0 or self[i] == 0:
285  quotient.y[i] = 0.
286  else:
287  quotient.y[i] = self[i] / denominator[i]
288  quotient.yerr[i] = quotient[i]
289  quotient.yerr[i] *= math.sqrt((num_yerr[i] / self[i]) ** 2 +
290  (den_yerr[i] / denominator[i]) ** 2)
291  if quotient.yerr[i] > quotient[i]:
292  quotient.yerr[i] = quotient[i]
293  quotient.yerr = [quotient.yerr, quotient.yerr]
294  return quotient
295  def divide_wilson(self, denominator):
296  """Return an efficiency plot with Wilson score interval errors."""
297  if len(self) != len(denominator):
298  raise TypeError("Cannot divide %s with %i bins by "
299  "%s with %i bins." %
300  (denominator.name, len(denominator),
301  self.name, len(self)))
302  eff, upper_err, lower_err = wilson_interval(self.y, denominator.y)
303  quotient = copy.deepcopy(self)
304  quotient.y = eff
305  quotient.yerr = [lower_err, upper_err]
306  return quotient
307 
309  """
310  A container to hold Hist objects for plotting together.
311 
312  When plotting, the title and the x and y labels of the last Hist added
313  will be used unless specified otherwise in the constructor.
314  """
315  def __init__(self, hists=None, title=None, xlabel=None, ylabel=None):
316  self.hists = []
317  self.kwargs = []
318  self.title = title
319  self.xlabel = xlabel
320  self.ylabel = ylabel
321  if hists:
322  for hist in hists:
323  self.add(hist)
324  def __getitem__(self, index):
325  """Return indexth hist: x.__getitem__(y) <==> x[y]"""
326  return self.hists[index]
327  def __setitem__(self, index, value):
328  """Replace indexth hist with value: x.__setitem__(i, y) <==> x[i]=y"""
329  self.hists[index] = value
330  def __len__(self):
331  """Return the number of hists in the stack: x.__len__() <==> len(x)"""
332  return len(self.hists)
333  def __iter__(self):
334  """Iterate through hists in the stack: x.__iter__() <==> iter(x)"""
335  return iter(self.hists)
336  def max(self):
337  """Return the value of the highest bin of all hists in the stack."""
338  maxes = [max(x) for x in self.hists]
339  try:
340  return max(maxes)
341  except ValueError:
342  return 0
343  def stackmax(self):
344  """Return the value of the highest bin in the addition of all hists."""
345  try:
346  return max([sum([h[i] for h in self.hists])
347  for i in range(self.hists[0].nbins)])
348  except:
349  print [h.nbins for h in self.hists]
350  def scale(self, factor):
351  """Scale all Hists by factor."""
352  for hist in self.hists:
353  hist.scale(factor)
354  def min(self, threshold=None):
355  """
356  Return the value of the lowest bin of all hists in the stack.
357 
358  If threshold is specified, only values above the threshold will be
359  considered.
360  """
361  mins = [x.min(threshold) for x in self.hists]
362  return min(mins)
363  def add(self, hist, **kwargs):
364  """
365  Add a Hist object to this stack.
366 
367  Any additional keyword arguments will be added to just this Hist
368  when the stack is plotted.
369  """
370  if "label" in kwargs:
371  hist.label = kwargs['label']
372  del kwargs['label']
373  if len(self) > 0:
374  if hist.xedges != self.hists[0].xedges:
375  raise ValueError("Cannot add %s to stack; all Hists must "
376  "have the same binning." % hist.name)
377  self.hists.append(hist)
378  self.kwargs.append(kwargs)
379 
380 
381 ################ Define functions and classes for navigating within ROOT
382 
384  """A wrapper for TFiles, allowing easier access to methods."""
385  def __init__(self, filename, name=None):
386  self.filename = filename
387  self.name = name or filename[:-5]
388  self.file = ROOT.TFile(filename, 'read')
389  if self.file.IsZombie():
390  raise ValueError("Error opening %s" % filename)
391  def cd(self, directory=''):
392  """Make directory the current directory."""
393  self.file.cd(directory)
394  def get(self, object_name, path=None, type1D=Hist, type2D=Hist2D):
395  """Return a Hist object from the given path within this file."""
396  if not path:
397  path = os.path.dirname(object_name)
398  object_name = os.path.basename(object_name)
399  try:
400  roothist = self.file.GetDirectory(path).Get(object_name)
401  except ReferenceError, e:
402  raise ReferenceError(e)
403  try:
404  return type2D(roothist)
405  except TypeError:
406  return type1D(roothist)
407 
408 def ls(directory=None):
409  """Return a python list of ROOT object names from the given directory."""
410  if directory == None:
411  keys = ROOT.gDirectory.GetListOfKeys()
412  else:
413  keys = ROOT.gDirectory.GetDirectory(directory).GetListOfKeys()
414  key = keys[0]
415  names = []
416  while key:
417  obj = key.ReadObj()
418  key = keys.After(key)
419  names.append(obj.GetName())
420  return names
421 
422 def pwd():
423  """Return ROOT's present working directory."""
424  return ROOT.gDirectory.GetPath()
425 
426 def get(object_name):
427  """Return a Hist object with the given name."""
428  return Hist(ROOT.gDirectory.Get(object_name))
429 
430 
431 ################ Define additional helping functions
432 
433 def loadROOT(batch=True):
434  ## We need to temporarily change sys.argv so that ROOT doesn't intercept
435  ## options from the command-line
436  saved_argv = sys.argv[:]
437  argstring = ' '.join(sys.argv)
438  sys.argv = [sys.argv[0]]
439  try:
440  import ROOT
441  except ImportError:
442  print """\
443 The program was unable to access PyROOT. Usually, this just requires switching
444 to the same major version of python that used when compiling ROOT. To
445 determine which version that is, try the following command:
446  root -config 2>&1 | tr ' ' '\\n' | egrep 'python|PYTHON'
447 If this is different from the python version you are currently using, try
448 changing your PATH to point to the new one."""
449  sys.exit(1)
450  ## Enter batch mode, unless outputting to C macros
451  ## There is a bug in pyROOT that fails to export colors in batch mode
452  if batch:
453  ROOT.gROOT.SetBatch()
454  ROOT.gErrorIgnoreLevel = ROOT.kWarning
455  ## PyROOT picks up ~/.rootlogon if it exists, but not ./rootlogon.C
456  if os.path.exists('rootlogon.C'):
457  ROOT.gROOT.Macro('rootlogon.C')
458  sys.argv = saved_argv[:]
459  return ROOT
460 
461 def replace(string, replacements):
462  """
463  Modify a string based on a list of patterns and substitutions.
464 
465  replacements should be a list of two-entry tuples, the first entry giving
466  a string to search for and the second entry giving the string with which
467  to replace it. If replacements includes a pattern entry containing
468  'use_regexp', then all patterns will be treated as regular expressions
469  using re.sub.
470  """
471  if not replacements:
472  return string
473  if 'use_regexp' in [x for x,y in replacements]:
474  for pattern, repl in [x for x in replacements
475  if x[0] != 'use_regexp']:
476  string = re.sub(pattern, repl, string)
477  else:
478  for pattern, repl in replacements:
479  string = string.replace(pattern, repl)
480  if re.match(_all_whitespace_string, string):
481  return ""
482  return string
483 
484 def process_bin_labels(binlabels):
485  has_labels = False
486  for binlabel in binlabels:
487  if binlabel:
488  has_labels = True
489  if has_labels:
490  return binlabels
491  else:
492  return None
493 
494 def wilson_interval(numerator_array, denominator_array):
495  eff, upper_err, lower_err = [], [], []
496  for n, d in zip(numerator_array, denominator_array):
497  try:
498  p = float(n) / d
499  s = math.sqrt(p * (1 - p) / d + 1 / (4 * d * d))
500  t = p + 1 / (2 * d)
501  eff.append(p)
502  upper_err.append(-p + 1/(1 + 1/d) * (t + s))
503  lower_err.append(+p - 1/(1 + 1/d) * (t - s))
504  except ZeroDivisionError:
505  eff.append(0)
506  upper_err.append(0)
507  lower_err.append(0)
508  return eff, upper_err, lower_err
509 
511  import os
512  try:
513  num_processors = os.sysconf('SC_NPROCESSORS_ONLN')
514  except:
515  try:
516  num_processors = os.environ['NUMBER_OF_PROCESSORS']
517  except:
518  num_processors = 1
519  return num_processors
520 
521 def testfile():
522  outfile = ROOT.TFile("test.root", "recreate")
523  for i in range(4):
524  d = outfile.mkdir("dir%i" % (i + 1))
525  d.cd()
526  for j in range(4):
527  hist = ROOT.TH1F("hist%i" % (j + 1), "A Histogram", 10, 0, 10)
528  hist.Fill(j)
529  hist.Write()
530  outfile.Write()
531  return outfile
532 
533 #### Functions for globbing within root files
534 
535 glob_magic_check = re.compile('[*?[]')
536 
538  return glob_magic_check.search(s) is not None
539 
540 # These 2 helper functions non-recursively glob inside a literal directory.
541 # They return a list of basenames. `_rootglob1` accepts a pattern while
542 # `_rootglob0` takes a literal basename (so it only has to check for its
543 # existence).
544 
545 def _rootglob1(tdirectory, dirname, pattern):
546  if not tdirectory.GetDirectory(dirname):
547  return []
548  names = [key.GetName() for key in
549  tdirectory.GetDirectory(dirname).GetListOfKeys()]
550  return fnmatch.filter(names, pattern)
551 
552 def _rootglob0(tdirectory, dirname, basename):
553  if tdirectory.Get(os.path.join(dirname, basename)):
554  return [basename]
555  return []
556 
557 def rootglob(tdirectory, pathname):
558  """Return a list of paths matching a pathname pattern.
559 
560  The pattern may contain simple shell-style wildcards a la fnmatch.
561 
562  >>> import rootplot.utilities
563  >>> f = rootplot.utilities.testfile()
564  >>> rootglob(f, '*')
565  ['dir1', 'dir2', 'dir3', 'dir4']
566  >>> rootglob(f, 'dir1/*')
567  ['dir1/hist1', 'dir1/hist2', 'dir1/hist3', 'dir1/hist4']
568  >>> rootglob(f, '*/hist1')
569  ['dir1/hist1', 'dir2/hist1', 'dir3/hist1', 'dir4/hist1']
570  >>> rootglob(f, 'dir1/hist[1-2]')
571  ['dir1/hist1', 'dir1/hist2']
572  """
573  return list(irootglob(tdirectory, pathname))
574 
575 def irootglob(tdirectory, pathname):
576  """Return an iterator which yields the paths matching a pathname pattern.
577 
578  The pattern may contain simple shell-style wildcards a la fnmatch.
579 
580  """
581  if not has_glob_magic(pathname):
582  if tdirectory.Get(pathname):
583  yield pathname
584  return
585  dirname, basename = os.path.split(pathname)
586  if has_glob_magic(dirname):
587  dirs = irootglob(tdirectory, dirname)
588  else:
589  dirs = [dirname]
590  if has_glob_magic(basename):
591  glob_in_dir = _rootglob1
592  else:
593  glob_in_dir = _rootglob0
594  for dirname in dirs:
595  for name in glob_in_dir(tdirectory, dirname, basename):
596  yield os.path.join(dirname, name)
597 
598 if __name__ == '__main__':
599  import doctest
600  doctest.testmod()
def loadROOT
Define additional helping functions.
Definition: utilities.py:433
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
list object
Definition: dbtoconf.py:77
double split
Definition: MVATrainer.cc:139
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run