ATLAS Offline Software
EventSelectionConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
4 from AsgAnalysisAlgorithms.AsgAnalysisConfig import makeEventCutFlowConfig
5 
6 class EventSelectionMergerConfig(ConfigBlock):
7  """ConfigBlock for merging the output of various selection streams"""
8 
9  def __init__(self):
10  super(EventSelectionMergerConfig, self).__init__()
11  self.addOption('selections', [], type=list,
12  info="the selection decisions (list of strings) to unify into a "
13  "final decision (internally: selection_1 || selection_2 || ...). "
14  "The default is [] (empty list).")
15  self.addOption('noFilter', False, type=bool,
16  info="do not apply an event filter. The default is False, i.e. "
17  "remove events not passing the full list of selection cuts.")
18 
19  def makeAlgs(self, config):
20  alg = config.createAlgorithm('CP::SaveFilterAlg', 'EventSelectionMerger')
21  alg.FilterDescription = 'events passing at least one EventSelection algorithm'
22  alg.eventDecisionOutputDecoration = 'ignore_anySelection_%SYS%'
23  alg.selection = '||'.join([sel+',as_char' for sel in self.selections if sel])
24  alg.noFilter = self.noFilter
25  alg.selectionName = 'pass_anySelection_%SYS%'
26  alg.decorationName = 'ntuplepass_anySelection_%SYS%'
27 
28 class EventSelectionConfig(ConfigBlock):
29  """ConfigBlock for interpreting text-based event selections"""
30 
31  def __init__(self, name=''):
32  super(EventSelectionConfig, self).__init__()
33  self.addOption('name', name, type=str,
34  noneAction='error',
35  info="the name of the event selection, used to uniquely identify "
36  "the EventSelectionConfig block.")
37  self.addOption('electrons', "", type=str,
38  info="the input electron container, with a possible selection, in "
39  "the format container or container.selection. The default is '' "
40  "(empty string).")
41  self.addOption('muons', "", type=str,
42  info="the input muon container, with a possible selection, in the "
43  "format container or container.selection. The default is '' "
44  "(empty string).")
45  self.addOption('jets', "", type=str,
46  info="the input jet container, with a possible selection, in the "
47  "format container or container.selection. The default is '' "
48  "(empty string).")
49  self.addOption('largeRjets', "", type=str,
50  info="the large-R jet container, with a possible selection, in "
51  "the format container or container.selection. The default is '' "
52  "(empty string).")
53  self.addOption('photons', "", type=str,
54  info="the input photon container, with a possible selection, in "
55  "the format container or container.selection. The default is '' "
56  "(empty string).")
57  self.addOption('taus', "", type=str,
58  info="the input tau-jet container, with a possible selection, in "
59  "the format container or container.selection. The default is '' "
60  "(empty string).")
61  self.addOption('met', "", type=str,
62  info="he input MET container. The default is '' (empty string).")
63  #TODO: add info string
64  self.addOption('metTerm', "Final", type=str,
65  info="")
66  self.addOption('btagDecoration', "", type=str,
67  info="the b-tagging decoration to use when defining b-jets. "
68  "The default is '' (empty string).")
69  self.addOption('preselection', "", type=str,
70  info="the event-wise selection flag to start this event selection "
71  "from. The default is '' (empty string).")
72  self.addOption('selectionCuts', "", type=str,
73  noneAction='error',
74  info="a single string listing one selection cut per line.")
75  self.addOption('noFilter', False, type=bool,
76  info="do not apply an event filter. The default is False, i.e. "
77  "remove events not passing the full list of selection cuts.")
78  self.addOption('debugMode', False, type=bool,
79  info="whether to create an output branch for every single line "
80  "of the selection cuts. The default is False (only saves the"
81  " final decision).")
82  self.step = 0
84  self.cutflow = []
85  self.name = name
86 
87  def makeAlgs(self, config):
88  # need to re-initialize here to deal with multiple passes
89  self.step = 0
90  # initialize the pre-selection
91  self.currentDecoration = self.preselection
92  # re-initialize the cutflow
93  self.cutflow = []
94  # read the selection cuts
95  if self.selectionCuts is None:
96  raise ValueError ("[EventSelectionConfig] You must provide the 'selectionCuts' option to 'EventSelectionConfig': "
97  "a single string where each line represents a different selection cut to apply in order.")
98  for line in self.selectionCuts.split("\n"):
99  self.interpret(line, config)
100  config.addEventCutFlow(self.name, self.getCutflow())
101 
102  def interpret(self, text, cfg):
103  text = text.strip()
104  if not text:
105  return
106  if text.startswith("#"):
107  return
108  self.step += 1
109  if "EL_N" in text.split():
110  self.add_NEL_selector(text, cfg)
111  elif "MU_N" in text.split():
112  self.add_NMU_selector(text, cfg)
113  elif "SUM_EL_N_MU_N" in text.split():
114  self.add_SUMNELNMU_selector(text, cfg)
115  elif "JET_N" in text.split():
116  self.add_NJET_selector(text, cfg)
117  elif "JET_N_BTAG" in text.split():
118  self.add_NBJET_selector(text, cfg)
119  elif "PH_N" in text.split():
120  self.add_NPH_selector(text, cfg)
121  elif "TAU_N" in text.split():
122  self.add_NTAU_selector(text, cfg)
123  elif "LJET_N" in text.split():
124  self.add_NLJET_selector(text, cfg)
125  elif "MET" in text.split():
126  self.add_MET_selector(text, cfg)
127  elif "MWT" in text.split():
128  self.add_MWT_selector(text, cfg)
129  elif "MET+MWT" in text.split():
130  self.add_METMWT_selector(text, cfg)
131  elif "MLL" in text.split():
132  self.add_MLL_selector(text, cfg)
133  elif "MLLWINDOW" in text.split():
134  self.add_MLLWINDOW_selector(text, cfg)
135  elif "OS" in text.split():
136  self.add_OS_selector(text, cfg)
137  elif "SS" in text.split():
138  self.add_SS_selector(text, cfg)
139  elif "MLL_OSSF" in text.split():
140  self.add_MLL_OSSF_selector(text, cfg)
141  elif "LJETMASS_N" in text.split():
142  self.add_NLJETMASS_selector(text, cfg)
143  elif "LJETMASSWINDOW_N" in text.split():
144  self.add_NLJETMASSWINDOW_selector(text, cfg)
145  elif "SAVE" in text.split():
146  self.add_SAVE(text, cfg)
147  elif "IMPORT" in text.split():
148  self.add_IMPORT(text, cfg)
149  elif "EVENTFLAG" in text.split():
150  self.add_EVENTFLAG(text, cfg)
151  elif "GLOBALTRIGMATCH" in text.split():
152  self.add_GLOBALTRIGMATCH(text, cfg)
153  else:
154  raise ValueError (f"[EventSelectionConfig] The following selection cut is not recognised! --> {text}")
155 
156  def raise_misconfig(self, text, keyword):
157  raise ValueError (f"[EventSelectionConfig] Misconfiguration! Check {keyword} in: {text}")
158 
159  def raise_missinginput(self, collection):
160  raise ValueError (f"[EventSelectionConfig] Misconfiguration! Missing input collection for {collection}")
161 
162  def check_float(self, test, requirePositive=True):
163  try:
164  value = float(test)
165  if not requirePositive or value >= 0:
166  return value
167  else:
168  raise ValueError (f"[EventSelectionConfig] Misconfiguration! Float {test} is not positive!")
169  except ValueError:
170  raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be a float, not {type(test)}!")
171 
172  def check_int(self, test, requirePositive=True):
173  try:
174  value = int(test)
175  if value == float(test):
176  if not requirePositive or value >= 0:
177  return value
178  else:
179  raise ValueError (f"[EventSelectionConfig] Misconfiguration! Int {test} us not positive!")
180  else:
181  raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be an int, not a float!")
182  except ValueError:
183  raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be an int, not {type(test)}")
184 
185  def check_string(self, test):
186  if not isinstance(test, str):
187  raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be a string, not a number!")
188  else:
189  return test
190 
191  def check_sign(self, test):
192  mapping = {
193  "<" : "LT",
194  ">" : "GT",
195  "==": "EQ",
196  ">=": "GE",
197  "<=": "LE"
198  }
199  try:
200  return mapping[test]
201  except KeyError:
202  raise KeyError (f"[EventSelectionConfig] Misconfiguration! {test} should be one of {list(mapping.keys())}")
203 
204  def check_btagging(self, test):
205  test = test.split(":")
206  if len(test) != 2:
207  raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be provided as 'btagger:btagWP'")
208  else:
209  return test
210 
211  def getCutflow(self):
212  return self.cutflow
213 
214  def setDecorationName(self, algorithm, config, decoration):
215  self.cutflow.append( decoration )
216  if algorithm is not None:
217  algorithm.decorationName = f'{decoration},as_char'
218  self.currentDecoration = decoration
219  if self.debugMode:
220  config.addOutputVar('EventInfo', decoration, decoration.split("_%SYS%")[0])
221  else:
222  if self.currentDecoration:
223  self.currentDecoration += '&&' + decoration
224  else:
225  self.currentDecoration = decoration
226  config.addSelection('EventInfo', '', decoration)
227  return
228 
229  def checkDecorationName(self, decoration):
230  if decoration == '':
231  return decoration
232  decoration = decoration.split("&&")
233  decoration = [sub + ',as_char' if ',as_char' not in sub else sub for sub in decoration]
234  return '&&'.join(decoration)
235 
236  def add_IMPORT(self, text, config):
237  # this is used to import a previous selection
238  items = text.split()
239  if items[0] != "IMPORT":
240  self.raise_misconfig(text, "IMPORT")
241  if len(items) != 2:
242  self.raise_misconfig(text, "number of arguments")
243  region = self.check_string(items[1])
244  if not self.currentDecoration:
245  self.currentDecoration = f'pass_{region}_%SYS%,as_char'
246  else:
247  self.currentDecoration = f'{self.currentDecoration},as_char&&pass_{region}_%SYS%'
248  # for the cutflow, we need to retrieve all the cuts corresponding to this IMPORT
249  imported_cuts = [cut for cut in config.getSelectionCutFlow('EventInfo', '') if cut.startswith(region)]
250  self.cutflow += imported_cuts
251  return
252 
253  def add_NEL_selector(self, text, config):
254  items = text.split()
255  if items[0] != "EL_N":
256  self.raise_misconfig(text, "EL_N")
257  if len(items) != 4 and len(items) != 5:
258  self.raise_misconfig(text, "number of arguments")
259  if not self.electrons:
260  self.raise_missinginput("electrons")
261  thisalg = f'{self.name}_NEL_{self.step}'
262  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
263  alg.particles, alg.objectSelection = config.readNameAndSelection(self.electrons)
264  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
265  if len(items) == 4:
266  alg.minPt = self.check_float(items[1])
267  alg.sign = self.check_sign(items[2])
268  alg.count = self.check_int(items[3])
269  elif len(items) == 5:
270  extraSel = self.check_string(items[1])
271  if alg.objectSelection:
272  alg.objectSelection += "&&" + config.getFullSelection(self.electrons.split(".")[0], extraSel)
273  else:
274  alg.objectSelection = config.getFullSelection(self.electrons.split(".")[0], extraSel)
275  alg.minPt = self.check_float(items[2])
276  alg.sign = self.check_sign(items[3])
277  alg.count = self.check_int(items[4])
278  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
279  return
280 
281  def add_NMU_selector(self, text, config):
282  items = text.split()
283  if items[0] != "MU_N":
284  self.raise_misconfig(text, "MU_N")
285  if len(items) != 4 and len(items) != 5:
286  self.raise_misconfig(text, "number of arguments")
287  if not self.muons:
288  self.raise_missinginput("muons")
289  thisalg = f'{self.name}_NMU_{self.step}'
290  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
291  alg.particles, alg.objectSelection = config.readNameAndSelection(self.muons)
292  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
293  if len(items) == 4:
294  alg.minPt = self.check_float(items[1])
295  alg.sign = self.check_sign(items[2])
296  alg.count = self.check_int(items[3])
297  elif len(items) == 5:
298  extraSel = self.check_string(items[1])
299  if alg.objectSelection:
300  alg.objectSelection += "&&" + config.getFullSelection(self.muons.split(".")[0], extraSel)
301  else:
302  alg.objectSelection = config.getFullSelection(self.muons.split(".")[0], extraSel)
303  alg.minPt = self.check_float(items[2])
304  alg.sign = self.check_sign(items[3])
305  alg.count = self.check_int(items[4])
306  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
307  return
308 
309  def add_SUMNELNMU_selector(self, text, config):
310  items = text.split()
311  if items[0] != "SUM_EL_N_MU_N":
312  self.raise_misconfig(text, "SUM_EL_N_MU_N")
313  if len(items) != 4 and len(items) != 5:
314  self.raise_misconfig(text, "number of arguments")
315  if not self.electrons and not self.muons:
316  self.raise_missinginput("electrons or muons")
317  thisalg = f'{self.name}_SUMNELNMU_{self.step}'
318  alg = config.createAlgorithm('CP::SumNElNMuPtSelectorAlg', thisalg)
319  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
320  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
321  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
322  if len(items) == 4:
323  alg.minPtEl = self.check_float(items[1])
324  alg.minPtMu = self.check_float(items[1])
325  alg.sign = self.check_sign(items[2])
326  alg.count = self.check_int(items[3])
327  elif len(items) == 5:
328  alg.minPtEl = self.check_float(items[1])
329  alg.minPtMu = self.check_float(items[2])
330  alg.sign = self.check_sign(items[3])
331  alg.count = self.check_int(items[4])
332  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
333  return
334 
335  def add_NJET_selector(self, text, config):
336  items = text.split()
337  if items[0] != "JET_N":
338  self.raise_misconfig(text, "JET_N")
339  if len(items) != 4 and len(items) != 5:
340  self.raise_misconfig(text, "number of arguments")
341  if not self.jets:
342  self.raise_missinginput("jets")
343  thisalg = f'{self.name}_NJET_{self.step}'
344  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
345  alg.particles, alg.objectSelection = config.readNameAndSelection(self.jets)
346  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
347  if len(items) == 4:
348  alg.minPt = self.check_float(items[1])
349  alg.sign = self.check_sign(items[2])
350  alg.count = self.check_int(items[3])
351  elif len(items) == 5:
352  extraSel = self.check_string(items[1])
353  if alg.objectSelection:
354  alg.objectSelection += "&&" + config.getFullSelection(self.jets.split(".")[0], extraSel)
355  else:
356  alg.objectSelection = config.getFullSelection(self.jets.split(".")[0], extraSel)
357  alg.minPt = self.check_float(items[2])
358  alg.sign = self.check_sign(items[3])
359  alg.count = self.check_int(items[4])
360  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
361  return
362 
363  def add_NBJET_selector(self, text, config):
364  items = text.split()
365  if items[0] != "JET_N_BTAG":
366  self.raise_misconfig(text, "JET_N_BTAG")
367  if len(items) != 3 and len(items) != 4:
368  self.raise_misconfig(text, "number of arguments")
369  if not self.jets:
370  self.raise_missinginput("jets")
371  thisalg = f'{self.name}_NBJET_{self.step}'
372  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
373  particles, selection = config.readNameAndSelection(self.jets)
374  alg.particles = particles
375  alg.objectSelection = f'{selection}&&{self.btagDecoration},as_char'
376  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
377  if len(items) == 3:
378  alg.sign = self.check_sign(items[1])
379  alg.count = self.check_int(items[2])
380  elif len(items) == 4:
381  btagger, btagWP = self.check_btagging(items[1])
382  customBtag = f'ftag_select_{btagger}_{btagWP}'
383  alg.objectSelection = f'{selection}&&{customBtag},as_char'
384  alg.sign = self.check_sign(items[2])
385  alg.count = self.check_int(items[3])
386  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
387  return
388 
389  def add_NPH_selector(self, text, config):
390  items = text.split()
391  if items[0] != "PH_N":
392  self.raise_misconfig(text, "PH_N")
393  if len(items) != 4 and len(items) != 5:
394  self.raise_misconfig(text, "number of arguments")
395  if not self.photons:
396  self.raise_missinginput("photons")
397  thisalg = f'{self.name}_NPH_{self.step}'
398  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
399  alg.particles, alg.objectSelection = config.readNameAndSelection(self.photons)
400  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
401  if len(items) == 4:
402  alg.minPt = self.check_float(items[1])
403  alg.sign = self.check_sign(items[2])
404  alg.count = self.check_int(items[3])
405  elif len(items) == 5:
406  extraSel = self.check_string(items[1])
407  if alg.objectSelection:
408  alg.objectSelection += "&&" + config.getFullSelection(self.photons.split(".")[0], extraSel)
409  else:
410  alg.objectSelection = config.getFullSelection(self.photons.split(".")[0], extraSel)
411  alg.minPt = self.check_float(items[2])
412  alg.sign = self.check_sign(items[3])
413  alg.count = self.check_int(items[4])
414  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
415  return
416 
417  def add_NTAU_selector(self, text, config):
418  items = text.split()
419  if items[0] != "TAU_N":
420  self.raise_misconfig(text, "TAU_N")
421  if len(items) != 4 and len(items) != 5:
422  self.raise_misconfig(text, "number of arguments")
423  if not self.taus:
424  self.raise_missinginput("taus")
425  thisalg = f'{self.name}_NTAU_{self.step}'
426  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
427  alg.particles, alg.objectSelection = config.readNameAndSelection(self.taus)
428  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
429  if len(items) == 4:
430  alg.minPt = self.check_float(items[1])
431  alg.sign = self.check_sign(items[2])
432  alg.count = self.check_int(items[3])
433  elif len(items) == 5:
434  extraSel = self.check_string(items[1])
435  if alg.objectSelection:
436  alg.objectSelection += "&&" + config.getFullSelection(self.taus.split(".")[0], extraSel)
437  else:
438  alg.objectSelection = config.getFullSelection(self.taus.split(".")[0], extraSel)
439  alg.minPt = self.check_float(items[2])
440  alg.sign = self.check_sign(items[3])
441  alg.count = self.check_int(items[4])
442  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
443  return
444 
445  def add_NLJET_selector(self, text, config):
446  items = text.split()
447  if items[0] != "LJET_N":
448  self.raise_misconfig(text, "LJET_N")
449  if len(items) != 4 and len(items) != 5:
450  self.raise_misconfig(text, "number of arguments")
451  thisalg = f'{self.name}_NLJET_{self.step}'
452  alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
453  alg.particles, alg.objectSelection = config.readNameAndSelection(self.largeRjets)
454  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
455  if len(items) == 4:
456  alg.minPt = self.check_float(items[1])
457  alg.sign = self.check_sign(items[2])
458  alg.count = self.check_int(items[3])
459  elif len(items) == 5:
460  extraSel = self.check_string(items[1])
461  if alg.objectSelection:
462  alg.objectSelection += "&&" + config.getFullSelection(self.largeRjets.split(".")[0], extraSel)
463  else:
464  alg.objectSelection = config.getFullSelection(self.largeRjets.split(".")[0], extraSel)
465  alg.minPt = self.check_float(items[2])
466  alg.sign = self.check_sign(items[3])
467  alg.count = self.check_int(items[4])
468  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
469  return
470 
471  def add_NLJETMASS_selector(self, text, config):
472  items = text.split()
473  if items[0] != "LJETMASS_N":
474  self.raise_misconfig(text, "LJETMASS_N")
475  if len(items) != 4 and len(items) != 5:
476  self.raise_misconfig(text, "number of arguments")
477  thisalg = f'{self.name}_NLJETMASS_{self.step}'
478  alg = config.createAlgorithm('CP::NObjectMassSelectorAlg', thisalg)
479  alg.particles, alg.objectSelection = config.readNameAndSelection(self.largeRjets)
480  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
481  if len(items) == 4:
482  alg.minMass = self.check_float(items[1])
483  alg.sign = self.check_sign(items[2])
484  alg.count = self.check_int(items[3])
485  elif len(items) == 5:
486  extraSel = self.check_string(items[1])
487  if alg.objectSelection:
488  alg.objectSelection += "&&" + config.getFullSelection(self.largeRjets.split(".")[0], extraSel)
489  else:
490  alg.objectSelection = config.getFullSelection(self.largeRjets.split(".")[0], extraSel)
491  alg.minMass = self.check_float(items[2])
492  alg.sign = self.check_sign(items[3])
493  alg.count = self.check_int(items[4])
494  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
495  return
496 
497  def add_NLJETMASSWINDOW_selector(self, text, config):
498  items = text.split()
499  if items[0] != "LJETMASSWINDOW_N":
500  self.raise_misconfig(text, "LJETMASSWINDOW_N")
501  if len(items) != 5 and len(items) != 6 and len(items) != 7:
502  self.raise_misconfig(text, "number of arguments")
503  thisalg = f'{self.name}_NLJETMASSWINDOW_{self.step}'
504  alg = config.createAlgorithm('CP::NLargeRJetMassWindowSelectorAlg', thisalg)
505  alg.ljets, alg.ljetSelection = config.readNameAndSelection(self.largeRjets)
506  if len(items) == 5 or (len(items) == 6 and "veto" in items):
507  alg.lowMass = self.check_float(items[1])
508  alg.highMass = self.check_float(items[2])
509  alg.sign = self.check_sign(items[3])
510  alg.count = self.check_int(items[4])
511  alg.vetoMode = (len(items) == 6 and self.check_string(items[5]) == "veto")
512  elif (len(items) == 6 and "veto" not in items) or len(items) == 7:
513  extraSel = self.check_string(items[1])
514  if alg.ljetSelection:
515  alg.ljetSelection += "&&" + config.getFullSelection(self.largeRjets.split(".")[0], extraSel)
516  else:
517  alg.ljetSelection = config.getFullSelection(self.largeRjets.split(".")[0], extraSel)
518  alg.lowMass = self.check_float(items[2])
519  alg.highMass = self.check_float(items[3])
520  alg.sign = self.check_sign(items[4])
521  alg.count = self.check_int(items[5])
522  alg.vetoMode = (len(items) ==7 and self.check_string(items[6]) == "veto")
523  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
524  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
525  return
526 
527  def add_MET_selector(self, text, config):
528  items = text.split()
529  if items[0] != "MET":
530  self.raise_misconfig(text, "MET")
531  if len(items) != 3:
532  self.raise_misconfig(text, "number of arguments")
533  if not self.met:
534  self.raise_missinginput("MET")
535  thisalg = f'{self.name}_MET_{self.step}'
536  alg = config.createAlgorithm('CP::MissingETSelectorAlg', thisalg)
537  alg.met = config.readName(self.met)
538  alg.metTerm = self.metTerm
539  alg.sign = self.check_sign(items[1])
540  alg.refMET = self.check_float(items[2])
541  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
542  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
543  return
544 
545  def add_MWT_selector(self, text, config):
546  items = text.split()
547  if items[0] != "MWT":
548  self.raise_misconfig(text, "MWT")
549  if len(items) != 3:
550  self.raise_misconfig(text, "number of arguments")
551  if not self.electrons and not self.muons:
552  self.raise_missinginput("electrons or muons")
553  thisalg = f'{self.name}_MWT_{self.step}'
554  alg = config.createAlgorithm('CP::TransverseMassSelectorAlg', thisalg)
555  alg.met = config.readName(self.met)
556  alg.metTerm = self.metTerm
557  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
558  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
559  alg.sign = self.check_sign(items[1])
560  alg.refMWT = self.check_float(items[2])
561  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
562  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
563  return
564 
565  def add_METMWT_selector(self, text, config):
566  items = text.split()
567  if items[0] != "MET+MWT":
568  self.raise_misconfig(text, "MET+MWT")
569  if len(items) != 3:
570  self.raise_misconfig(text, "number of arguments")
571  if not self.met:
572  self.raise_missinginput("MET")
573  if not self.electrons and not self.muons:
574  self.raise_missinginput("electrons or muons")
575  thisalg = f'{self.name}_METMWT_{self.step}'
576  alg = config.createAlgorithm('CP::MissingETPlusTransverseMassSelectorAlg', thisalg)
577  alg.met = config.readName(self.met)
578  alg.metTerm = self.metTerm
579  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
580  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
581  alg.sign = self.check_sign(items[1])
582  alg.refMETMWT = self.check_float(items[2])
583  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
584  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
585  return
586 
587  def add_MLL_selector(self, text, config):
588  items = text.split()
589  if items[0] != "MLL":
590  self.raise_misconfig(text, "MLL")
591  if len(items) != 3:
592  self.raise_misconfig(text, "number of arguments")
593  if not self.electrons and not self.muons:
594  self.raise_missinginput("electrons or muons")
595  thisalg = f'{self.name}_MLL_{self.step}'
596  alg = config.createAlgorithm('CP::DileptonInvariantMassSelectorAlg', thisalg)
597  if self.electrons:
598  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
599  if self.muons:
600  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
601  alg.sign = self.check_sign(items[1])
602  alg.refMLL = self.check_float(items[2])
603  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
604  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
605  return
606 
607  def add_MLLWINDOW_selector(self, text, config):
608  items = text.split()
609  if items[0] != "MLLWINDOW":
610  self.raise_misconfig(text, "MLLWINDOW")
611  if len(items) != 3 and len(items) != 4:
612  self.raise_misconfig(text, "number of arguments")
613  if not self.electrons and not self.muons:
614  self.raise_missinginput("electrons or muons")
615  thisalg = f'{self.name}_MLLWINDOW_{self.step}'
616  alg = config.createAlgorithm('CP::DileptonInvariantMassWindowSelectorAlg', thisalg)
617  if self.electrons:
618  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
619  if self.muons:
620  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
621  alg.lowMLL = self.check_float(items[1])
622  alg.highMLL = self.check_float(items[2])
623  alg.vetoMode = (len(items) == 4 and self.check_string(items[3]) == "veto")
624  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
625  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
626  return
627 
628  def add_OS_selector(self, text, config):
629  items = text.split()
630  if len(items) != 1:
631  self.raise_misconfig(text, "number of arguments")
632  if not self.electrons and not self.muons:
633  self.raise_missinginput("electrons or muons")
634  thisalg = f'{self.name}_OS_{self.step}'
635  alg = config.createAlgorithm('CP::ChargeSelectorAlg', thisalg)
636  if self.electrons:
637  if "Particle" in self.electrons or "Truth" in self.electrons:
638  alg.truthElectrons, alg.truthElectronSelection = config.readNameAndSelection(self.electrons)
639  else:
640  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
641  if self.muons:
642  if "Particle" in self.muons or "Truth" in self.muons:
643  alg.truthMuons, alg.truthMuonSelection = config.readNameAndSelection(self.muons)
644  else:
645  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
646  alg.OS = True
647  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
648  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
649  return
650 
651  def add_SS_selector(self, text, config):
652  items = text.split()
653  if len(items) != 1:
654  self.raise_misconfig(text, "number of arguments")
655  if not self.electrons and not self.muons:
656  self.raise_missinginput("electrons or muons")
657  thisalg = f'{self.name}_SS_{self.step}'
658  alg = config.createAlgorithm('CP::ChargeSelectorAlg', thisalg)
659  if self.electrons:
660  if "Particle" in self.electrons or "Truth" in self.electrons:
661  alg.truthElectrons, alg.truthElectronSelection = config.readNameAndSelection(self.electrons)
662  else:
663  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
664  if self.muons:
665  if "Particle" in self.muons or "Truth" in self.muons:
666  alg.truthMuons, alg.truthMuonSelection = config.readNameAndSelection(self.muons)
667  else:
668  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
669  alg.OS = False
670  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
671  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
672  return
673 
674  def add_MLL_OSSF_selector(self, text, config):
675  items = text.split()
676  if items[0] != "MLL_OSSF":
677  self.raise_misconfig(text, "MLL_OSSF")
678  if len(items) != 3 and len(items) != 4:
679  self.raise_misconfig(text, "number of arguments")
680  if not self.electrons and not self.muons:
681  self.raise_missinginput("electrons or muons")
682  thisalg = f'{self.name}_MLL_OSSF_{self.step}'
683  alg = config.createAlgorithm('CP::DileptonOSSFInvariantMassWindowSelectorAlg', thisalg)
684  if self.electrons:
685  if "Particle" in self.electrons or "Truth" in self.electrons:
686  alg.truthElectrons, alg.truthElectronSelection = config.readNameAndSelection(self.electrons)
687  else:
688  alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
689  if self.muons:
690  if "Particle" in self.muons or "Truth" in self.muons:
691  alg.truthMuons, alg.truthMuonSelection = config.readNameAndSelection(self.muons)
692  else:
693  alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
694  alg.lowMll = self.check_float(items[1])
695  alg.highMll = self.check_float(items[2])
696  alg.vetoMode = (len(items) == 4 and self.check_string(items[3]) == "veto")
697  alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
698  self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
699  return
700 
701  def add_EVENTFLAG(self, text, config):
702  items = text.split()
703  if items[0] != "EVENTFLAG":
704  self.raise_misconfig(text, "EVENTFLAG")
705  if len(items) != 2:
706  self.raise_misconfig(text, "number of arguments")
707  existingDecoration = self.check_string(items[1])
708  self.setDecorationName(None, config, existingDecoration)
709  return
710 
711  def add_GLOBALTRIGMATCH(self, text, config):
712  items = text.split()
713  if items[0] != "GLOBALTRIGMATCH":
714  self.raise_misconfig(text, "GLOBALTRIGMATCH")
715  if len(items) != 1:
716  self.raise_misconfig(text, "number of arguments")
717  self.setDecorationName(None, config, "globalTriggerMatch_dontsave_%SYS%,as_char")
718  return
719 
720  def add_SAVE(self, text, config):
721  items = text.split()
722  if items[0] != "SAVE":
723  self.raise_misconfig(text, "SAVE")
724  if len(items) != 1:
725  self.raise_misconfig(text, "number of arguments")
726  thisalg = f'{self.name}_SAVE'
727  alg = config.createAlgorithm('CP::SaveFilterAlg', thisalg)
728  alg.FilterDescription = f'events passing < {self.name} >'
729  alg.eventDecisionOutputDecoration = f'ignore_{self.name}_%SYS%'
730  alg.selection = self.checkDecorationName(self.currentDecoration)
731  alg.noFilter = self.noFilter
732  alg.selectionName = f'pass_{self.name}_%SYS%,as_char' # this one is used as a selection
733  alg.decorationName = f'ntuplepass_{self.name}_%SYS%' # this one is saved to file
734  config.addOutputVar('EventInfo', f'ntuplepass_{self.name}_%SYS%', f'pass_{self.name}')
735  return
736 
738  name,
739  electrons=None, muons=None, jets=None,
740  largeRjets=None,
741  photons=None, taus=None, met=None, metTerm=None,
742  btagDecoration=None, preselection=None,
743  selectionCuts=None, noFilter=None,
744  debugMode=None, cutFlowHistograms=None):
745  """Create an event selection config block
746 
747  Keyword arguments:
748  name -- the name defining this selection
749  electrons -- the electron container and selection
750  muons -- the muon container and selection
751  jets -- the jet container and selection
752  largeRjets -- the large-R jet container and selection
753  photons -- the photon container and selection
754  taus -- the tau-jet container and selection
755  met -- the MET container
756  metTerm -- the MET term to use (e.g. 'Final', 'NonInt')
757  btagDecoration -- the b-tagging decoration to use when defining b-jets
758  preselection -- optional event-wise selection flag to start from
759  selectionCuts -- a string listing one selection cut per line
760  noFilter -- whether to disable the event filter
761  debugMode -- enables saving all intermediate decorations
762  cutFlowHistograms -- whether to toggle event cutflow histograms per systematic
763  """
764 
765  config = EventSelectionConfig(name)
766  config.setOptionValue ('electrons', electrons)
767  config.setOptionValue ('muons', muons)
768  config.setOptionValue ('jets', jets)
769  config.setOptionValue ('largeRjets', largeRjets)
770  config.setOptionValue ('photons', photons)
771  config.setOptionValue ('taus', taus)
772  config.setOptionValue ('met', met)
773  config.setOptionValue ('metTerm', metTerm)
774  config.setOptionValue ('btagDecoration', btagDecoration)
775  config.setOptionValue ('preselection', preselection)
776  config.setOptionValue ('selectionCuts', selectionCuts)
777  config.setOptionValue ('noFilter', noFilter)
778  config.setOptionValue ('debugMode', debugMode)
779  seq.append(config)
780 
781  # add event cutflow algorithm
782  if cutFlowHistograms:
783  makeEventCutFlowConfig(seq, 'EventInfo', selectionName='', postfix=name,
784  customSelections=name)
785 
787  electrons=None, muons=None, jets=None,
788  largeRjets=None,
789  photons=None, taus=None, met=None, metTerm=None,
790  btagDecoration=None, preselection=None,
791  selectionCutsDict=None, noFilter=None,
792  debugMode=None, cutFlowHistograms=None):
793  """Create multiple event selection config blocks
794 
795  Keyword arguments:
796  electrons -- the electron container and selection
797  muons -- the muon container and selection
798  jets -- the jet container and selection
799  largeRjets -- the large-R jet container and selection
800  photons -- the photon container and selection
801  taus -- the tau-jet container and selection
802  met -- the MET container
803  metTerm -- the MET term to use (e.g. 'Final', 'NonInt')
804  btagDecoration -- the b-tagging decoration to use when defining b-jets
805  preselection -- optional event-wise selection flag to start from
806  selectionCutsDict -- a dictionary with key the name of the selection and value a string listing one selection cut per line
807  noFilter -- whether to disable the event filter
808  debugMode -- enables saving all intermediate decorations
809  cutFlowHistograms -- whether to toggle event cutflow histograms per region and per systematic
810  """
811 
812  # handle the case where a user is only providing one selection
813  if len(list(selectionCutsDict.keys())) == 1:
814  name, selectionCuts = list(selectionCutsDict.items())[0]
815  makeEventSelectionConfig(seq, name, electrons, muons, jets, largeRjets, photons, taus, met, metTerm, btagDecoration, preselection, selectionCuts, noFilter=noFilter, debugMode=debugMode, cutFlowHistograms=cutFlowHistograms)
816  return
817 
818  # first, we generate all the individual event selections
819  # !!! it's important to pass noFilter=True, to avoid applying the individual filters in series
820  for name, selectionCuts in selectionCutsDict.items():
821  makeEventSelectionConfig(seq, name, electrons, muons, jets, largeRjets, photons, taus, met, metTerm, btagDecoration, preselection, selectionCuts, noFilter=True, debugMode=debugMode, cutFlowHistograms=cutFlowHistograms)
822 
823  # now we are ready to collect all the filters and apply their logical OR
824  # !!! subregions (name starts with "SUB") are not used in the final filtering
825  config = EventSelectionMergerConfig()
826  config.setOptionValue ('selections', [f'pass_{name}_%SYS%' for name in selectionCutsDict.keys() if not name.startswith("SUB")])
827  config.setOptionValue ('noFilter', noFilter)
828  seq.append(config)
python.EventSelectionConfig.EventSelectionConfig.add_MWT_selector
def add_MWT_selector(self, text, config)
Definition: EventSelectionConfig.py:545
python.EventSelectionConfig.EventSelectionConfig.__init__
def __init__(self, name='')
Definition: EventSelectionConfig.py:31
python.EventSelectionConfig.EventSelectionConfig.add_MLLWINDOW_selector
def add_MLLWINDOW_selector(self, text, config)
Definition: EventSelectionConfig.py:607
python.EventSelectionConfig.EventSelectionConfig.check_float
def check_float(self, test, requirePositive=True)
Definition: EventSelectionConfig.py:162
python.EventSelectionConfig.EventSelectionConfig.check_string
def check_string(self, test)
Definition: EventSelectionConfig.py:185
python.EventSelectionConfig.EventSelectionConfig
Definition: EventSelectionConfig.py:28
python.EventSelectionConfig.EventSelectionConfig.add_SS_selector
def add_SS_selector(self, text, config)
Definition: EventSelectionConfig.py:651
python.EventSelectionConfig.EventSelectionConfig.add_NMU_selector
def add_NMU_selector(self, text, config)
Definition: EventSelectionConfig.py:281
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.EventSelectionConfig.EventSelectionConfig.setDecorationName
def setDecorationName(self, algorithm, config, decoration)
Definition: EventSelectionConfig.py:214
python.AsgAnalysisConfig.makeEventCutFlowConfig
def makeEventCutFlowConfig(seq, containerName, *postfix=None, selectionName, customSelections=None)
Definition: AsgAnalysisConfig.py:673
python.EventSelectionConfig.EventSelectionConfig.add_SAVE
def add_SAVE(self, text, config)
Definition: EventSelectionConfig.py:720
python.EventSelectionConfig.EventSelectionConfig.add_MET_selector
def add_MET_selector(self, text, config)
Definition: EventSelectionConfig.py:527
python.EventSelectionConfig.EventSelectionConfig.add_GLOBALTRIGMATCH
def add_GLOBALTRIGMATCH(self, text, config)
Definition: EventSelectionConfig.py:711
python.EventSelectionConfig.EventSelectionConfig.interpret
def interpret(self, text, cfg)
Definition: EventSelectionConfig.py:102
python.EventSelectionConfig.EventSelectionConfig.currentDecoration
currentDecoration
Definition: EventSelectionConfig.py:83
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.EventSelectionConfig.EventSelectionMergerConfig
Definition: EventSelectionConfig.py:6
python.EventSelectionConfig.EventSelectionConfig.add_NTAU_selector
def add_NTAU_selector(self, text, config)
Definition: EventSelectionConfig.py:417
python.EventSelectionConfig.EventSelectionMergerConfig.__init__
def __init__(self)
Definition: EventSelectionConfig.py:9
python.EventSelectionConfig.EventSelectionConfig.checkDecorationName
def checkDecorationName(self, decoration)
Definition: EventSelectionConfig.py:229
python.EventSelectionConfig.EventSelectionConfig.getCutflow
def getCutflow(self)
Definition: EventSelectionConfig.py:211
python.EventSelectionConfig.EventSelectionMergerConfig.makeAlgs
def makeAlgs(self, config)
Definition: EventSelectionConfig.py:19
python.EventSelectionConfig.EventSelectionConfig.add_EVENTFLAG
def add_EVENTFLAG(self, text, config)
Definition: EventSelectionConfig.py:701
python.EventSelectionConfig.EventSelectionConfig.name
name
Definition: EventSelectionConfig.py:85
python.EventSelectionConfig.EventSelectionConfig.check_int
def check_int(self, test, requirePositive=True)
Definition: EventSelectionConfig.py:172
python.EventSelectionConfig.makeMultipleEventSelectionConfigs
def makeMultipleEventSelectionConfigs(seq, electrons=None, muons=None, jets=None, largeRjets=None, photons=None, taus=None, met=None, metTerm=None, btagDecoration=None, preselection=None, selectionCutsDict=None, noFilter=None, debugMode=None, cutFlowHistograms=None)
Definition: EventSelectionConfig.py:786
python.EventSelectionConfig.EventSelectionConfig.add_OS_selector
def add_OS_selector(self, text, config)
Definition: EventSelectionConfig.py:628
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.EventSelectionConfig.EventSelectionConfig.raise_misconfig
def raise_misconfig(self, text, keyword)
Definition: EventSelectionConfig.py:156
python.EventSelectionConfig.EventSelectionConfig.add_NLJETMASS_selector
def add_NLJETMASS_selector(self, text, config)
Definition: EventSelectionConfig.py:471
python.EventSelectionConfig.EventSelectionConfig.add_IMPORT
def add_IMPORT(self, text, config)
Definition: EventSelectionConfig.py:236
python.EventSelectionConfig.EventSelectionConfig.add_NLJET_selector
def add_NLJET_selector(self, text, config)
Definition: EventSelectionConfig.py:445
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.EventSelectionConfig.EventSelectionConfig.step
step
Definition: EventSelectionConfig.py:82
python.EventSelectionConfig.EventSelectionConfig.makeAlgs
def makeAlgs(self, config)
Definition: EventSelectionConfig.py:87
python.EventSelectionConfig.EventSelectionConfig.check_btagging
def check_btagging(self, test)
Definition: EventSelectionConfig.py:204
python.EventSelectionConfig.EventSelectionConfig.add_METMWT_selector
def add_METMWT_selector(self, text, config)
Definition: EventSelectionConfig.py:565
python.EventSelectionConfig.EventSelectionConfig.raise_missinginput
def raise_missinginput(self, collection)
Definition: EventSelectionConfig.py:159
python.EventSelectionConfig.EventSelectionConfig.add_NPH_selector
def add_NPH_selector(self, text, config)
Definition: EventSelectionConfig.py:389
python.EventSelectionConfig.EventSelectionConfig.add_NBJET_selector
def add_NBJET_selector(self, text, config)
Definition: EventSelectionConfig.py:363
python.EventSelectionConfig.EventSelectionConfig.add_NEL_selector
def add_NEL_selector(self, text, config)
Definition: EventSelectionConfig.py:253
python.EventSelectionConfig.EventSelectionConfig.cutflow
cutflow
Definition: EventSelectionConfig.py:84
python.EventSelectionConfig.EventSelectionConfig.add_SUMNELNMU_selector
def add_SUMNELNMU_selector(self, text, config)
Definition: EventSelectionConfig.py:309
python.EventSelectionConfig.EventSelectionConfig.check_sign
def check_sign(self, test)
Definition: EventSelectionConfig.py:191
python.EventSelectionConfig.EventSelectionConfig.add_NLJETMASSWINDOW_selector
def add_NLJETMASSWINDOW_selector(self, text, config)
Definition: EventSelectionConfig.py:497
python.EventSelectionConfig.EventSelectionConfig.add_MLL_OSSF_selector
def add_MLL_OSSF_selector(self, text, config)
Definition: EventSelectionConfig.py:674
python.EventSelectionConfig.EventSelectionConfig.add_MLL_selector
def add_MLL_selector(self, text, config)
Definition: EventSelectionConfig.py:587
python.EventSelectionConfig.makeEventSelectionConfig
def makeEventSelectionConfig(seq, name, electrons=None, muons=None, jets=None, largeRjets=None, photons=None, taus=None, met=None, metTerm=None, btagDecoration=None, preselection=None, selectionCuts=None, noFilter=None, debugMode=None, cutFlowHistograms=None)
Definition: EventSelectionConfig.py:737
python.EventSelectionConfig.EventSelectionConfig.add_NJET_selector
def add_NJET_selector(self, text, config)
Definition: EventSelectionConfig.py:335
readCCLHist.float
float
Definition: readCCLHist.py:83
Trk::split
@ split
Definition: LayerMaterialProperties.h:38