3from functools
import partial
5from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
6from AnalysisAlgorithmsConfig.ConfigSequence
import groupBlocks
7from AsgAnalysisAlgorithms.AsgAnalysisConfig
import EventCutFlowBlock
8from AnalysisAlgorithmsConfig.ConfigAccumulator
import DataType
12 """ConfigBlock for merging the output of various selection streams"""
15 super(EventSelectionMergerConfig, self).
__init__()
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.")
24 """Return the instance name for this block"""
32 selections = config.getContainerMeta(
'EventInfo',
'eventSelectionNames',
34 selections = [sel
for sel
in selections
if not sel.startswith(
"pass_SUB")]
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%'
47 """ConfigBlock for interpreting text-based event selections"""
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"),
61 super(EventSelectionConfig, self).
__init__()
62 self.setBlockName(
'EventSelection')
63 self.addOption(
'selectionName',
'', type=str,
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 "
94 self.addOption(
'selectionCuts',
"", type=str,
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"
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.")
111 """Return the instance name for this block"""
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."""
132 "OS": partial(self.
_add_charge, osMode=
True, tag=
"OS"),
133 "SS": partial(self.
_add_charge, osMode=
False, tag=
"SS"),
140 for kw, (attr, tag)
in self.
_NOBJECT.items():
145 existing = config.getContainerMeta(
'EventInfo',
'eventSelectionNames', defaultValue=[])
146 config.setContainerMeta(
'EventInfo',
'eventSelectionNames',
147 existing + [f
'pass_{self.selectionName}_%SYS%'], allowOverwrite=
True)
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"):
165 if not text
or text.startswith(
"#"):
168 keyword = text.split()[0]
171 raise ValueError (f
"[EventSelectionConfig] The following selection cut is not recognised! --> {text}")
179 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Check {keyword} in: {text}")
182 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Missing input collection for {collection}")
185 """Validate the leading keyword and the number of arguments."""
186 if items[0] != keyword:
188 if len(items)
not in validCounts:
194 if not requirePositive
or value >= 0:
197 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Float {test} is not positive!")
199 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be a float, not {type(test)}!")
204 if value == float(test):
205 if not requirePositive
or value >= 0:
208 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Int {test} us not positive!")
210 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be an int, not a float!")
212 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be an int, not {type(test)}")
215 if not isinstance(test, str):
216 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be a string, not a number!")
231 raise KeyError (f
"[EventSelectionConfig] Misconfiguration! {test} should be one of {list(mapping.keys())}")
234 test = test.split(
":")
236 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be provided as 'btagger:btagWP'")
242 values = test.split(
"!")
244 "B":
"GhostBHadronsFinalCount",
245 "C":
"GhostCHadronsFinalCount",
246 "T":
"GhostTQuarksFinalCount",
247 "W":
"GhostWBosonsCount",
248 "Z":
"GhostZBosonsCount",
249 "H":
"GhostHBosonsCount",
250 "TAU":
"GhostTausFinalCount"
252 return [ghost_map.get(value.upper(), value)
for value
in values]
262 self.
cutflow.append( decoration )
263 if algorithm
is not None:
264 algorithm.decorationName = f
'{decoration},as_char'
267 config.addOutputVar(
'EventInfo', decoration, decoration.split(
"_%SYS%")[0])
273 config.addSelection(
'EventInfo',
'', 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)
285 return oldSelection +
"&&" + config.getFullSelection(container, newSelection)
287 return config.getFullSelection(container, newSelection)
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
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)."""
307 config, container, alg.objectSelection, extraSel)
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)
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."""
333 spec = getattr(self, attr)
336 if len(items)
not in (4, 5):
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"):
345 items, config, alg, spec.split(
".")[0])
356 self.
currentDecoration = f
'{self.currentDecoration},as_char&&pass_{region}_%SYS%'
358 imported_cuts = [cut
for cut
in config.getSelectionCutFlow(
'EventInfo',
'')
if cut.startswith(region)]
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'
376 elif len(items) == 4:
379 customBtag = f
'ftag_select_{btagger}_{btagWP}'
380 alg.objectSelection = f
'{selection}&&{customBtag},as_char' if selection
else f
'{customBtag},as_char'
386 elif len(items) == 5:
389 customBtag = f
'ftag_select_{btagger}_{btagWP}'
390 alg.objectSelection = f
'{selection}&&{customBtag},as_char' if selection
else f
'{customBtag},as_char'
399 self.
_check_args(items,
"SUM_EL_N_MU_N", (4, 5, 7))
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)
413 elif len(items) == 5:
418 elif len(items) == 7:
432 self.
_check_args(items,
"SUM_EL_N_MU_N_TAU_N", (4, 6, 9))
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)
448 elif len(items) == 6:
454 elif len(items) == 9:
472 thisalg = f
'{self.selectionName}_NLJETMASS_{self.step}'
473 alg = config.createAlgorithm(
'CP::NObjectMassSelectorAlg', thisalg)
474 alg.particles, alg.objectSelection = config.readNameAndSelection(self.largeRjets)
477 items, config, alg, self.largeRjets.
split(
".")[0])
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):
493 alg.vetoMode = vetoMode
494 elif (len(items) == 6
and not vetoMode)
or len(items) == 7:
501 alg.vetoMode = vetoMode
509 thisalg = f
'{self.selectionName}_NJETGHOST_{self.step}'
510 alg = config.createAlgorithm(
'CP::JetNGhostSelectorAlg', thisalg)
511 alg.jets, alg.jetSelection = config.readNameAndSelection(self.
jets)
513 alg.ghost = ghosts[0]
519 elif len(items) == 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)
534 alg.ghost = ghosts[0]
540 elif len(items) == 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]))
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
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)
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)
619 thisalg = f
'{self.selectionName}_MLL_{self.step}'
620 alg = config.createAlgorithm(
'CP::DileptonInvariantMassSelectorAlg', thisalg)
622 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
624 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
637 thisalg = f
'{self.selectionName}_MLLWINDOW_{self.step}'
638 alg = config.createAlgorithm(
'CP::DileptonInvariantMassWindowSelectorAlg', thisalg)
640 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
642 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
646 alg.vetoMode = (len(items) == 4
and self.
check_string(items[3]).lower() ==
"veto")
652 """Builder shared by OS and SS: same algorithm, opposite charge mode."""
654 if not items
or len(items) > 4:
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):
663 (
'electrons',
'electronSelection'),
664 (
'truthElectrons',
'truthElectronSelection'))
665 if self.
muons and (allLeptons
or "mu" in items):
667 (
'muons',
'muonSelection'),
668 (
'truthMuons',
'truthMuonSelection'))
669 if self.
taus and (allLeptons
or "tau" in items):
671 (
'taus',
'tauSelection'),
672 (
'truthTaus',
'truthTauSelection'))
683 thisalg = f
'{self.selectionName}_MLL_OSSF_{self.step}'
684 alg = config.createAlgorithm(
'CP::DileptonOSSFInvariantMassWindowSelectorAlg', thisalg)
687 (
'electrons',
'electronSelection'),
688 (
'truthElectrons',
'truthElectronSelection'))
691 (
'muons',
'muonSelection'),
692 (
'truthMuons',
'truthMuonSelection'))
696 alg.vetoMode = (len(items) == 4
and self.
check_string(items[3]).lower() ==
"veto")
715 self.
setDecorationName(
None, config, f
"globalTriggerMatch{postfix}_%SYS%,as_char")
721 thisalg = f
'{self.selectionName}_RUN_NUMBER_{self.step}'
722 alg = config.createAlgorithm(
'CP::RunNumberSelectorAlg', thisalg)
725 alg.useRandomRunNumber = config.dataType()
is not DataType.Data
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%'
739 alg.selectionName = f
'pass_{self.selectionName}_%SYS%,as_char'
740 alg.decorationName = f
'ntuplepass_{self.selectionName}_%SYS%'
741 config.addOutputVar(
'EventInfo', f
'ntuplepass_{self.selectionName}_%SYS%', f
'pass_{self.selectionName}')
748 seq.append(EventCutFlowBlock())
setDecorationName(self, algorithm, config, decoration)
add_SUMNELNMU_selector(self, text, config)
raise_missinginput(self, collection)
extendObjectSelection(self, config, container, oldSelection, newSelection)
add_RUNNUMBER(self, text, config)
add_MLL_selector(self, text, config)
check_int(self, test, requirePositive=True)
add_METMWT_selector(self, text, config)
add_EVENTFLAG(self, text, config)
_add_charge(self, text, config, *, osMode, tag)
add_NLJETGHOST_selector(self, text, config)
add_NOBJ_selector(self, text, config)
add_MWT_selector(self, text, config)
_route_lepton(self, alg, config, spec, reco, truth)
add_SUMNLEPTONS_selector(self, text, config)
add_IMPORT(self, text, config)
_add_nobject(self, text, config, *, attr, tag)
add_GLOBALTRIGMATCH(self, text, config)
raise_misconfig(self, text, keyword)
add_NBJET_selector(self, text, config)
checkDecorationName(self, decoration)
add_NLJETMASS_selector(self, text, config)
_val_sign_count(self, items, config, alg, container)
check_float(self, test, requirePositive=True)
interpret(self, text, cfg)
_check_args(self, items, keyword, validCounts)
_maybe_dressed(self, alg, *specs)
add_MET_selector(self, text, config)
check_btagging(self, test)
add_MLLWINDOW_selector(self, text, config)
add_SAVE(self, text, config)
add_NJETGHOST_selector(self, text, config)
add_NLJETMASSWINDOW_selector(self, text, config)
add_MLL_OSSF_selector(self, text, config)
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
std::vector< std::string > split(const std::string &s, const std::string &t=":")
void handler(int sig)
signal handler