ATLAS Offline Software
Loading...
Searching...
No Matches
EventSelectionConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3from functools import partial
4
5from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
6from AnalysisAlgorithmsConfig.ConfigSequence import groupBlocks
7from AsgAnalysisAlgorithms.AsgAnalysisConfig import EventCutFlowBlock
8from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
9
10
11class EventSelectionMergerConfig(ConfigBlock):
12 """ConfigBlock for merging the output of various selection streams"""
13
14 def __init__(self):
15 super(EventSelectionMergerConfig, self).__init__()
16 self._instance_number = EventSelectionMergerConfig.get_instance_count()
17 self.setBlockName('EventSelectionMerger')
18 self.addDependency('EventSelection', required=True)
19 self.addOption('noFilter', False, type=bool,
20 info="do not apply an event filter, i.e. setting it to `False` "
21 "removes events not passing the full list of selection cuts.")
22
23 def instanceName (self) :
24 """Return the instance name for this block"""
25 return '' # There is only ever one instance of this block
26
27 def makeAlgs(self, config):
28 # Only the first instance runs; all others are no-ops
29 if self._instance_number != 1:
30 return
31
32 selections = config.getContainerMeta('EventInfo', 'eventSelectionNames',
33 failOnMiss=True)
34 selections = [sel for sel in selections if not sel.startswith("pass_SUB")]
35
36 alg = config.createAlgorithm('CP::SaveFilterAlg',
37 'EventSelectionMerger' + selections[0].split("_%SYS%")[0])
38 alg.FilterDescription = 'events passing at least one EventSelection'
39 alg.eventDecisionOutputDecoration = 'ignore_anySelection_%SYS%'
40 alg.selection = '||'.join([sel + ',as_char' for sel in selections])
41 alg.noFilter = self.noFilter
42 alg.selectionName = 'pass_anySelection_%SYS%'
43 alg.decorationName = 'ntuplepass_anySelection_%SYS%'
44
45
46class EventSelectionConfig(ConfigBlock):
47 """ConfigBlock for interpreting text-based event selections"""
48
49 # N-object pT selectors that differ only by source container.
50 # keyword -> (container option, algorithm-name tag)
51 _NOBJECT = {
52 "EL_N": ("electrons", "NEL"),
53 "MU_N": ("muons", "NMU"),
54 "JET_N": ("jets", "NJET"),
55 "PH_N": ("photons", "NPH"),
56 "TAU_N": ("taus", "NTAU"),
57 "LJET_N": ("largeRjets", "NLJET"),
58 }
59
60 def __init__(self):
61 super(EventSelectionConfig, self).__init__()
62 self.setBlockName('EventSelection')
63 self.addOption('selectionName', '', type=str,
64 noneAction='error',
65 info="the name of the event selection, used to uniquely identify "
66 "the `EventSelectionConfig` block.")
67 self.addOption('electrons', "", type=str,
68 info="the input electron container, with a possible selection, in "
69 "the format `container` or `container.selection`.")
70 self.addOption('muons', "", type=str,
71 info="the input muon container, with a possible selection, in the "
72 "format `container` or `container.selection`.")
73 self.addOption('jets', "", type=str,
74 info="the input jet container, with a possible selection, in the "
75 "format `container` or `container.selection`.")
76 self.addOption('largeRjets', "", type=str,
77 info="the large-R jet container, with a possible selection, in "
78 "the format `container` or `container.selection`.")
79 self.addOption('photons', "", type=str,
80 info="the input photon container, with a possible selection, in "
81 "the format `container` or `container.selection`.")
82 self.addOption('taus', "", type=str,
83 info="the input tau-jet container, with a possible selection, in "
84 "the format `container` or `container.selection`.")
85 self.addOption('met', "", type=str,
86 info="the input MET container.")
87 self.addOption('metTerm', "Final", type=str,
88 info="the MET term to use when computing MET-based quantities.")
89 self.addOption('btagDecoration', "", type=str,
90 info="the b-tagging decoration to use when defining b-jets.")
91 self.addOption('preselection', "", type=str,
92 info="the event-wise selection flag to start this event selection "
93 "from.")
94 self.addOption('selectionCuts', "", type=str,
95 noneAction='error',
96 info="a single string listing one selection cut per line. "
97 "See [available keywords](https://topcptoolkit.docs.cern.ch/latest/settings/eventselection/#available-keywords).")
98 self.addOption('debugMode', False, type=bool,
99 info="whether to create an output branch for every single line "
100 "of the selection cuts. Setting it to `False` only saves the"
101 " final decision.")
102 self.addOption('useDressedProperties', True, type=bool,
103 info="whether to use dressed truth electron and truth muon "
104 "kinematics rather than simple 4-vector kinematics.")
105 self.step = 0
107 self.cutflow = []
109
110 def instanceName (self) :
111 """Return the instance name for this block"""
112 return self.selectionName
113
115 """Map each keyword to its handler. Dispatch is an exact lookup on the
116 first token, which removes the ordering fragility of token-membership."""
117 d = {
118 "JET_N_BTAG": self.add_NBJET_selector,
119 "JET_N_GHOST": self.add_NJETGHOST_selector,
120 "LJET_N_GHOST": self.add_NLJETGHOST_selector,
121 "LJETMASS_N": self.add_NLJETMASS_selector,
122 "LJETMASSWINDOW_N": self.add_NLJETMASSWINDOW_selector,
123 "OBJ_N": self.add_NOBJ_selector,
124 "SUM_EL_N_MU_N": self.add_SUMNELNMU_selector,
125 "SUM_EL_N_MU_N_TAU_N": self.add_SUMNLEPTONS_selector,
126 "MET": self.add_MET_selector,
127 "MWT": self.add_MWT_selector,
128 "MET+MWT": self.add_METMWT_selector,
129 "MLL": self.add_MLL_selector,
130 "MLLWINDOW": self.add_MLLWINDOW_selector,
131 "MLL_OSSF": self.add_MLL_OSSF_selector,
132 "OS": partial(self._add_charge, osMode=True, tag="OS"),
133 "SS": partial(self._add_charge, osMode=False, tag="SS"),
134 "SAVE": self.add_SAVE,
135 "IMPORT": self.add_IMPORT,
136 "EVENTFLAG": self.add_EVENTFLAG,
137 "GLOBALTRIGMATCH": self.add_GLOBALTRIGMATCH,
138 "RUN_NUMBER": self.add_RUNNUMBER,
139 }
140 for kw, (attr, tag) in self._NOBJECT.items():
141 d[kw] = partial(self._add_nobject, attr=attr, tag=tag)
142 return d
143
144 def makeAlgs(self, config):
145 existing = config.getContainerMeta('EventInfo', 'eventSelectionNames', defaultValue=[])
146 config.setContainerMeta('EventInfo', 'eventSelectionNames',
147 existing + [f'pass_{self.selectionName}_%SYS%'], allowOverwrite=True)
148
149 # need to re-initialize here to deal with multiple passes
150 self.step = 0
151 # initialize the pre-selection
152 self.currentDecoration = self.preselection
153 # re-initialize the cutflow
154 self.cutflow = []
155 # read the selection cuts
156 if self.selectionCuts is None:
157 raise ValueError ("[EventSelectionConfig] You must provide the 'selectionCuts' option to 'EventSelectionConfig': "
158 "a single string where each line represents a different selection cut to apply in order.")
159 for line in self.selectionCuts.split("\n"):
160 self.interpret(line, config)
161 config.addEventCutFlow(self.selectionName, self.getCutflow())
162
163 def interpret(self, text, cfg):
164 text = text.strip()
165 if not text or text.startswith("#"):
166 return
167 self.step += 1
168 keyword = text.split()[0]
169 handler = self._dispatch.get(keyword)
170 if handler is None:
171 raise ValueError (f"[EventSelectionConfig] The following selection cut is not recognised! --> {text}")
172 handler(text, cfg)
173
174 # ------------------------------------------------------------------ #
175 # validation helpers #
176 # ------------------------------------------------------------------ #
177
178 def raise_misconfig(self, text, keyword):
179 raise ValueError (f"[EventSelectionConfig] Misconfiguration! Check {keyword} in: {text}")
180
181 def raise_missinginput(self, collection):
182 raise ValueError (f"[EventSelectionConfig] Misconfiguration! Missing input collection for {collection}")
183
184 def _check_args(self, items, keyword, validCounts):
185 """Validate the leading keyword and the number of arguments."""
186 if items[0] != keyword:
187 self.raise_misconfig(' '.join(items), keyword)
188 if len(items) not in validCounts:
189 self.raise_misconfig(' '.join(items), "number of arguments")
190
191 def check_float(self, test, requirePositive=True):
192 try:
193 value = float(test)
194 if not requirePositive or value >= 0:
195 return value
196 else:
197 raise ValueError (f"[EventSelectionConfig] Misconfiguration! Float {test} is not positive!")
198 except ValueError:
199 raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be a float, not {type(test)}!")
200
201 def check_int(self, test, requirePositive=True):
202 try:
203 value = int(test)
204 if value == float(test):
205 if not requirePositive or value >= 0:
206 return value
207 else:
208 raise ValueError (f"[EventSelectionConfig] Misconfiguration! Int {test} us not positive!")
209 else:
210 raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be an int, not a float!")
211 except ValueError:
212 raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be an int, not {type(test)}")
213
214 def check_string(self, test):
215 if not isinstance(test, str):
216 raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be a string, not a number!")
217 else:
218 return test
219
220 def check_sign(self, test):
221 mapping = {
222 "<" : "LT",
223 ">" : "GT",
224 "==": "EQ",
225 ">=": "GE",
226 "<=": "LE"
227 }
228 try:
229 return mapping[test]
230 except KeyError:
231 raise KeyError (f"[EventSelectionConfig] Misconfiguration! {test} should be one of {list(mapping.keys())}")
232
233 def check_btagging(self, test):
234 test = test.split(":")
235 if len(test) != 2:
236 raise ValueError (f"[EventSelectionConfig] Misconfiguration! {test} should be provided as 'btagger:btagWP'")
237 else:
238 return test
239
240 def check_ghosts(self, test):
241 test = self.check_string(test)
242 values = test.split("!")
243 ghost_map = {
244 "B": "GhostBHadronsFinalCount",
245 "C": "GhostCHadronsFinalCount",
246 "T": "GhostTQuarksFinalCount",
247 "W": "GhostWBosonsCount",
248 "Z": "GhostZBosonsCount",
249 "H": "GhostHBosonsCount",
250 "TAU": "GhostTausFinalCount"
251 }
252 return [ghost_map.get(value.upper(), value) for value in values]
253
254 # ------------------------------------------------------------------ #
255 # decoration / selection bookkeeping #
256 # ------------------------------------------------------------------ #
257
258 def getCutflow(self):
259 return self.cutflow
260
261 def setDecorationName(self, algorithm, config, decoration):
262 self.cutflow.append( decoration )
263 if algorithm is not None:
264 algorithm.decorationName = f'{decoration},as_char'
265 self.currentDecoration = decoration
266 if self.debugMode:
267 config.addOutputVar('EventInfo', decoration, decoration.split("_%SYS%")[0])
268 else:
269 if self.currentDecoration:
270 self.currentDecoration += '&&' + decoration
271 else:
272 self.currentDecoration = decoration
273 config.addSelection('EventInfo', '', decoration)
274 return
275
276 def checkDecorationName(self, decoration):
277 if decoration == '':
278 return decoration
279 decoration = decoration.split("&&")
280 decoration = [sub + ',as_char' if ',as_char' not in sub else sub for sub in decoration]
281 return '&&'.join(decoration)
282
283 def extendObjectSelection(self, config, container, oldSelection, newSelection):
284 if oldSelection:
285 return oldSelection + "&&" + config.getFullSelection(container, newSelection)
286 else:
287 return config.getFullSelection(container, newSelection)
288
289 # ------------------------------------------------------------------ #
290 # shared selector helpers #
291 # ------------------------------------------------------------------ #
292
293 def _maybe_dressed(self, alg, *specs):
294 """Enable dressed kinematics when any of the given electron/muon
295 containers is a truth container. Dressed kinematics only exist for
296 truth electrons and muons, so only those specs should be passed here."""
297 if any(spec and ("Particle" in spec or "Truth" in spec) for spec in specs):
298 alg.useDressedProperties = self.useDressedProperties
299
300 def _val_sign_count(self, items, config, alg, container):
301 """Parse the trailing `[extraSel] value sign count` grammar (4 or 5
302 tokens), applying the optional extra object selection in place.
303 Returns (value, sign, count)."""
304 if len(items) == 5:
305 extraSel = self.check_string(items[1])
306 alg.objectSelection = self.extendObjectSelection(
307 config, container, alg.objectSelection, extraSel)
308 i = 2
309 else: # len == 4, already validated by the caller
310 i = 1
311 return (self.check_float(items[i]),
312 self.check_sign(items[i + 1]),
313 self.check_int(items[i + 2]))
314
315 def _route_lepton(self, alg, config, spec, reco, truth):
316 """Assign (name, selection) to the reco or truth handles of `alg`
317 depending on whether `spec` points to a truth container.
318 `reco`/`truth` are (nameAttr, selectionAttr) pairs."""
319 name, sel = config.readNameAndSelection(spec)
320 nameAttr, selAttr = truth if ("Particle" in spec or "Truth" in spec) else reco
321 setattr(alg, nameAttr, name)
322 setattr(alg, selAttr, sel)
323
324 # ------------------------------------------------------------------ #
325 # selector builders #
326 # ------------------------------------------------------------------ #
327
328 def _add_nobject(self, text, config, *, attr, tag):
329 """Generic builder for the N-object pT selectors (EL_N, MU_N, JET_N,
330 PH_N, TAU_N, LJET_N): identical except for the source container, which
331 is always required since the cut acts on it."""
332 items = text.split()
333 spec = getattr(self, attr)
334 if not spec:
335 self.raise_missinginput(attr)
336 if len(items) not in (4, 5):
337 self.raise_misconfig(text, "number of arguments")
338 thisalg = f'{self.selectionName}_{tag}_{self.step}'
339 alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
340 alg.particles, alg.objectSelection = config.readNameAndSelection(spec)
341 if attr in ("electrons", "muons"):
342 self._maybe_dressed(alg, spec)
343 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
344 alg.minPt, alg.sign, alg.count = self._val_sign_count(
345 items, config, alg, spec.split(".")[0])
346 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
347
348 def add_IMPORT(self, text, config):
349 # this is used to import a previous selection
350 items = text.split()
351 self._check_args(items, "IMPORT", (2,))
352 region = self.check_string(items[1])
353 if not self.currentDecoration:
354 self.currentDecoration = f'pass_{region}_%SYS%,as_char'
355 else:
356 self.currentDecoration = f'{self.currentDecoration},as_char&&pass_{region}_%SYS%'
357 # for the cutflow, we need to retrieve all the cuts corresponding to this IMPORT
358 imported_cuts = [cut for cut in config.getSelectionCutFlow('EventInfo', '') if cut.startswith(region)]
359 self.cutflow += imported_cuts
360 return
361
362 def add_NBJET_selector(self, text, config):
363 items = text.split()
364 self._check_args(items, "JET_N_BTAG", (3, 4, 5))
365 if not self.jets:
366 self.raise_missinginput("jets")
367 thisalg = f'{self.selectionName}_NBJET_{self.step}'
368 alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
369 particles, selection = config.readNameAndSelection(self.jets)
370 alg.particles = particles
371 alg.objectSelection = f'{selection}&&{self.btagDecoration},as_char' if selection else f'{self.btagDecoration},as_char'
372 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
373 if len(items) == 3:
374 alg.sign = self.check_sign(items[1])
375 alg.count = self.check_int(items[2])
376 elif len(items) == 4:
377 if ":" in text:
378 btagger, btagWP = self.check_btagging(items[1])
379 customBtag = f'ftag_select_{btagger}_{btagWP}'
380 alg.objectSelection = f'{selection}&&{customBtag},as_char' if selection else f'{customBtag},as_char'
381 else:
382 extraSel = self.check_string(items[1])
383 alg.objectSelection = self.extendObjectSelection(config, self.jets.split(".")[0], alg.objectSelection, extraSel)
384 alg.sign = self.check_sign(items[2])
385 alg.count = self.check_int(items[3])
386 elif len(items) == 5:
387 extraSel = self.check_string(items[1])
388 btagger, btagWP = self.check_btagging(items[2])
389 customBtag = f'ftag_select_{btagger}_{btagWP}'
390 alg.objectSelection = f'{selection}&&{customBtag},as_char' if selection else f'{customBtag},as_char'
391 alg.objectSelection = self.extendObjectSelection(config, self.jets.split(".")[0], alg.objectSelection, extraSel)
392 alg.sign = self.check_sign(items[3])
393 alg.count = self.check_int(items[4])
394 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
395 return
396
397 def add_SUMNELNMU_selector(self, text, config):
398 items = text.split()
399 self._check_args(items, "SUM_EL_N_MU_N", (4, 5, 7))
400 if not self.electrons and not self.muons:
401 self.raise_missinginput("electrons or muons")
402 thisalg = f'{self.selectionName}_SUMNELNMU_{self.step}'
403 alg = config.createAlgorithm('CP::SumNLeptonPtSelectorAlg', thisalg)
404 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
405 alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
406 self._maybe_dressed(alg, self.electrons, self.muons)
407 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
408 if len(items) == 4:
409 alg.minPtEl = self.check_float(items[1])
410 alg.minPtMu = self.check_float(items[1])
411 alg.sign = self.check_sign(items[2])
412 alg.count = self.check_int(items[3])
413 elif len(items) == 5:
414 alg.minPtEl = self.check_float(items[1])
415 alg.minPtMu = self.check_float(items[2])
416 alg.sign = self.check_sign(items[3])
417 alg.count = self.check_int(items[4])
418 elif len(items) == 7:
419 extraSelEl = self.check_string(items[1])
420 extraSelMu = self.check_string(items[2])
421 alg.electronSelection = self.extendObjectSelection(config, self.electrons.split(".")[0], alg.electronSelection, extraSelEl)
422 alg.muonSelection = self.extendObjectSelection(config, self.muons.split(".")[0], alg.muonSelection, extraSelMu)
423 alg.minPtEl = self.check_float(items[3])
424 alg.minPtMu = self.check_float(items[4])
425 alg.sign = self.check_sign(items[5])
426 alg.count = self.check_int(items[6])
427 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
428 return
429
430 def add_SUMNLEPTONS_selector(self, text, config):
431 items = text.split()
432 self._check_args(items, "SUM_EL_N_MU_N_TAU_N", (4, 6, 9))
433 if not self.electrons and not self.muons and not self.taus:
434 self.raise_missinginput("electrons, muons or taus")
435 thisalg = f'{self.selectionName}_SUMNLEPTONS_{self.step}'
436 alg = config.createAlgorithm('CP::SumNLeptonPtSelectorAlg', thisalg)
437 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
438 alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
439 alg.taus, alg.tauSelection = config.readNameAndSelection(self.taus)
440 self._maybe_dressed(alg, self.electrons, self.muons)
441 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
442 if len(items) == 4:
443 alg.minPtEl = self.check_float(items[1])
444 alg.minPtMu = self.check_float(items[1])
445 alg.minPtTau = self.check_float(items[1])
446 alg.sign = self.check_sign(items[2])
447 alg.count = self.check_int(items[3])
448 elif len(items) == 6:
449 alg.minPtEl = self.check_float(items[1])
450 alg.minPtMu = self.check_float(items[2])
451 alg.minPtTau = self.check_float(items[3])
452 alg.sign = self.check_sign(items[4])
453 alg.count = self.check_int(items[5])
454 elif len(items) == 9:
455 extraSelEl = self.check_string(items[1])
456 extraSelMu = self.check_string(items[2])
457 extraSelTau = self.check_string(items[3])
458 alg.electronSelection = self.extendObjectSelection(config, self.electrons.split(".")[0], alg.electronSelection, extraSelEl)
459 alg.muonSelection = self.extendObjectSelection(config, self.muons.split(".")[0], alg.muonSelection, extraSelMu)
460 alg.tauSelection = self.extendObjectSelection(config, self.taus.split(".")[0], alg.tauSelection, extraSelTau)
461 alg.minPtEl = self.check_float(items[4])
462 alg.minPtMu = self.check_float(items[5])
463 alg.minPtTau = self.check_float(items[6])
464 alg.sign = self.check_sign(items[7])
465 alg.count = self.check_int(items[8])
466 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
467 return
468
469 def add_NLJETMASS_selector(self, text, config):
470 items = text.split()
471 self._check_args(items, "LJETMASS_N", (4, 5))
472 thisalg = f'{self.selectionName}_NLJETMASS_{self.step}'
473 alg = config.createAlgorithm('CP::NObjectMassSelectorAlg', thisalg)
474 alg.particles, alg.objectSelection = config.readNameAndSelection(self.largeRjets)
475 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
476 alg.minMass, alg.sign, alg.count = self._val_sign_count(
477 items, config, alg, self.largeRjets.split(".")[0])
478 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
479 return
480
481 def add_NLJETMASSWINDOW_selector(self, text, config):
482 items = text.split()
483 self._check_args(items, "LJETMASSWINDOW_N", (5, 6, 7))
484 thisalg = f'{self.selectionName}_NLJETMASSWINDOW_{self.step}'
485 alg = config.createAlgorithm('CP::NLargeRJetMassWindowSelectorAlg', thisalg)
486 alg.ljets, alg.ljetSelection = config.readNameAndSelection(self.largeRjets)
487 vetoMode = items[-1] == 'veto' or items[-1] == 'VETO'
488 if len(items) == 5 or (len(items) == 6 and vetoMode):
489 alg.lowMass = self.check_float(items[1])
490 alg.highMass = self.check_float(items[2])
491 alg.sign = self.check_sign(items[3])
492 alg.count = self.check_int(items[4])
493 alg.vetoMode = vetoMode
494 elif (len(items) == 6 and not vetoMode) or len(items) == 7:
495 extraSel = self.check_string(items[1])
496 alg.ljetSelection = self.extendObjectSelection(config, self.largeRjets.split(".")[0], alg.ljetSelection, extraSel)
497 alg.lowMass = self.check_float(items[2])
498 alg.highMass = self.check_float(items[3])
499 alg.sign = self.check_sign(items[4])
500 alg.count = self.check_int(items[5])
501 alg.vetoMode = vetoMode
502 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
503 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
504 return
505
506 def add_NJETGHOST_selector(self, text, config):
507 items = text.split()
508 self._check_args(items, "JET_N_GHOST", (4, 5))
509 thisalg = f'{self.selectionName}_NJETGHOST_{self.step}'
510 alg = config.createAlgorithm('CP::JetNGhostSelectorAlg', thisalg)
511 alg.jets, alg.jetSelection = config.readNameAndSelection(self.jets)
512 ghosts = self.check_ghosts(items[1])
513 alg.ghost = ghosts[0]
514 if len(ghosts) > 1 :
515 alg.veto = ghosts[1]
516 if len(items) == 4:
517 alg.sign = self.check_sign(items[2])
518 alg.count = self.check_int(items[3])
519 elif len(items) == 5:
520 alg.minPt = self.check_float(items[2])
521 alg.sign = self.check_sign(items[3])
522 alg.count = self.check_int(items[4])
523 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
524 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
525 return
526
527 def add_NLJETGHOST_selector(self, text, config):
528 items = text.split()
529 self._check_args(items, "LJET_N_GHOST", (4, 5))
530 thisalg = f'{self.selectionName}_NLJETGHOST_{self.step}'
531 alg = config.createAlgorithm('CP::JetNGhostSelectorAlg', thisalg)
532 alg.jets, alg.jetSelection = config.readNameAndSelection(self.largeRjets)
533 ghosts = self.check_ghosts(items[1])
534 alg.ghost = ghosts[0]
535 if len(ghosts) > 1 :
536 alg.veto = ghosts[1]
537 if len(items) == 4:
538 alg.sign = self.check_sign(items[2])
539 alg.count = self.check_int(items[3])
540 elif len(items) == 5:
541 alg.minPt = self.check_float(items[2])
542 alg.sign = self.check_sign(items[3])
543 alg.count = self.check_int(items[4])
544 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
545 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
546 return
547
548 def add_NOBJ_selector(self, text, config):
549 items = text.split()
550 self._check_args(items, "OBJ_N", (5,))
551 thisalg = f'{self.selectionName}_NOBJ_{self.step}'
552 alg = config.createAlgorithm('CP::NObjectPtSelectorAlg', thisalg)
553 alg.particles, alg.objectSelection = config.readNameAndSelection(self.check_string(items[1]))
554 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
555 alg.minPt = self.check_float(items[2])
556 alg.sign = self.check_sign(items[3])
557 alg.count = self.check_int(items[4])
558 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
559 return
560
561 def add_MET_selector(self, text, config):
562 items = text.split()
563 self._check_args(items, "MET", (3,))
564 if not self.met:
565 self.raise_missinginput("MET")
566 thisalg = f'{self.selectionName}_MET_{self.step}'
567 alg = config.createAlgorithm('CP::MissingETSelectorAlg', thisalg)
568 alg.met = config.readName(self.met)
569 alg.metTerm = self.metTerm
570 alg.sign = self.check_sign(items[1])
571 alg.refMET = self.check_float(items[2])
572 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
573 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
574 return
575
576 def add_MWT_selector(self, text, config):
577 items = text.split()
578 self._check_args(items, "MWT", (3,))
579 if not self.electrons and not self.muons:
580 self.raise_missinginput("electrons or muons")
581 thisalg = f'{self.selectionName}_MWT_{self.step}'
582 alg = config.createAlgorithm('CP::TransverseMassSelectorAlg', thisalg)
583 alg.met = config.readName(self.met)
584 alg.metTerm = self.metTerm
585 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
586 alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
587 self._maybe_dressed(alg, self.electrons, self.muons)
588 alg.sign = self.check_sign(items[1])
589 alg.refMWT = self.check_float(items[2])
590 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
591 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
592 return
593
594 def add_METMWT_selector(self, text, config):
595 items = text.split()
596 self._check_args(items, "MET+MWT", (3,))
597 if not self.met:
598 self.raise_missinginput("MET")
599 if not self.electrons and not self.muons:
600 self.raise_missinginput("electrons or muons")
601 thisalg = f'{self.selectionName}_METMWT_{self.step}'
602 alg = config.createAlgorithm('CP::MissingETPlusTransverseMassSelectorAlg', thisalg)
603 alg.met = config.readName(self.met)
604 alg.metTerm = self.metTerm
605 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
606 alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
607 self._maybe_dressed(alg, self.electrons, self.muons)
608 alg.sign = self.check_sign(items[1])
609 alg.refMETMWT = self.check_float(items[2])
610 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
611 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
612 return
613
614 def add_MLL_selector(self, text, config):
615 items = text.split()
616 self._check_args(items, "MLL", (3,))
617 if not self.electrons and not self.muons:
618 self.raise_missinginput("electrons or muons")
619 thisalg = f'{self.selectionName}_MLL_{self.step}'
620 alg = config.createAlgorithm('CP::DileptonInvariantMassSelectorAlg', thisalg)
621 if self.electrons:
622 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
623 if self.muons:
624 alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
625 self._maybe_dressed(alg, self.electrons, self.muons)
626 alg.sign = self.check_sign(items[1])
627 alg.refMLL = self.check_float(items[2])
628 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
629 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
630 return
631
632 def add_MLLWINDOW_selector(self, text, config):
633 items = text.split()
634 self._check_args(items, "MLLWINDOW", (3, 4))
635 if not self.electrons and not self.muons:
636 self.raise_missinginput("electrons or muons")
637 thisalg = f'{self.selectionName}_MLLWINDOW_{self.step}'
638 alg = config.createAlgorithm('CP::DileptonInvariantMassWindowSelectorAlg', thisalg)
639 if self.electrons:
640 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.electrons)
641 if self.muons:
642 alg.muons, alg.muonSelection = config.readNameAndSelection(self.muons)
643 self._maybe_dressed(alg, self.electrons, self.muons)
644 alg.lowMLL = self.check_float(items[1])
645 alg.highMLL = self.check_float(items[2])
646 alg.vetoMode = (len(items) == 4 and self.check_string(items[3]).lower() == "veto")
647 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
648 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
649 return
650
651 def _add_charge(self, text, config, *, osMode, tag):
652 """Builder shared by OS and SS: same algorithm, opposite charge mode."""
653 items = text.split()
654 if not items or len(items) > 4:
655 self.raise_misconfig(text, "number of arguments")
656 if not self.electrons and not self.muons and not self.taus:
657 self.raise_missinginput("electrons or muons or taus")
658 thisalg = f'{self.selectionName}_{tag}_{self.step}'
659 alg = config.createAlgorithm('CP::ChargeSelectorAlg', thisalg)
660 allLeptons = (len(items) == 1)
661 if self.electrons and (allLeptons or "el" in items):
662 self._route_lepton(alg, config, self.electrons,
663 ('electrons', 'electronSelection'),
664 ('truthElectrons', 'truthElectronSelection'))
665 if self.muons and (allLeptons or "mu" in items):
666 self._route_lepton(alg, config, self.muons,
667 ('muons', 'muonSelection'),
668 ('truthMuons', 'truthMuonSelection'))
669 if self.taus and (allLeptons or "tau" in items):
670 self._route_lepton(alg, config, self.taus,
671 ('taus', 'tauSelection'),
672 ('truthTaus', 'truthTauSelection'))
673 alg.OS = osMode
674 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
675 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
676 return
677
678 def add_MLL_OSSF_selector(self, text, config):
679 items = text.split()
680 self._check_args(items, "MLL_OSSF", (3, 4))
681 if not self.electrons and not self.muons:
682 self.raise_missinginput("electrons or muons")
683 thisalg = f'{self.selectionName}_MLL_OSSF_{self.step}'
684 alg = config.createAlgorithm('CP::DileptonOSSFInvariantMassWindowSelectorAlg', thisalg)
685 if self.electrons:
686 self._route_lepton(alg, config, self.electrons,
687 ('electrons', 'electronSelection'),
688 ('truthElectrons', 'truthElectronSelection'))
689 if self.muons:
690 self._route_lepton(alg, config, self.muons,
691 ('muons', 'muonSelection'),
692 ('truthMuons', 'truthMuonSelection'))
693 self._maybe_dressed(alg, self.electrons, 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]).lower() == "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 self._check_args(items, "EVENTFLAG", (2,))
704 existingDecoration = self.check_string(items[1])
705 self.setDecorationName(None, config, existingDecoration)
706 return
707
708 def add_GLOBALTRIGMATCH(self, text, config):
709 items = text.split()
710 self._check_args(items, "GLOBALTRIGMATCH", (1, 2))
711 if len(items) == 1:
712 self.setDecorationName(None, config, "globalTriggerMatch_%SYS%,as_char")
713 else:
714 postfix = self.check_string(items[1])
715 self.setDecorationName(None, config, f"globalTriggerMatch{postfix}_%SYS%,as_char")
716 return
717
718 def add_RUNNUMBER(self, text, config):
719 items = text.split()
720 self._check_args(items, "RUN_NUMBER", (3,))
721 thisalg = f'{self.selectionName}_RUN_NUMBER_{self.step}'
722 alg = config.createAlgorithm('CP::RunNumberSelectorAlg', thisalg)
723 alg.sign = self.check_sign(items[1])
724 alg.runNumber = self.check_int(items[2])
725 alg.useRandomRunNumber = config.dataType() is not DataType.Data
726 alg.eventPreselection = self.checkDecorationName(self.currentDecoration)
727 self.setDecorationName(alg, config, f'{thisalg}_%SYS%')
728 return
729
730 def add_SAVE(self, text, config):
731 items = text.split()
732 self._check_args(items, "SAVE", (1,))
733 thisalg = f'{self.selectionName}_SAVE'
734 alg = config.createAlgorithm('CP::SaveFilterAlg', thisalg)
735 alg.FilterDescription = f'events passing < {self.selectionName} >'
736 alg.eventDecisionOutputDecoration = f'ignore_{self.selectionName}_%SYS%'
737 alg.selection = self.checkDecorationName(self.currentDecoration)
738 alg.noFilter = True
739 alg.selectionName = f'pass_{self.selectionName}_%SYS%,as_char' # this one is used as a selection
740 alg.decorationName = f'ntuplepass_{self.selectionName}_%SYS%' # this one is saved to file
741 config.addOutputVar('EventInfo', f'ntuplepass_{self.selectionName}_%SYS%', f'pass_{self.selectionName}')
742 return
743
744
745@groupBlocks
747 seq.append(EventSelectionConfig())
748 seq.append(EventCutFlowBlock())
749 seq.append(EventSelectionMergerConfig())
setDecorationName(self, algorithm, config, decoration)
extendObjectSelection(self, config, container, oldSelection, newSelection)
_add_charge(self, text, config, *, osMode, tag)
_route_lepton(self, alg, config, spec, reco, truth)
_val_sign_count(self, items, config, alg, container)
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:132
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179
void handler(int sig)
signal handler
Definition rmain.cxx:99