5from copy
import deepcopy
6from dataclasses
import dataclass
7from functools
import partial
9from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
10from AnalysisAlgorithmsConfig.ConfigSequence
import groupBlocks
11from AsgAnalysisAlgorithms.AsgAnalysisConfig
import EventCutFlowBlock
12from AnalysisAlgorithmsConfig.ConfigAccumulator
import DataType, ConfigDeprecationWarning
16 """Raised when an EXPR cut uses a syntactically valid but unimplemented
17 feature (an unknown variable or collection). Subclasses ValueError so the
18 framework's existing tolerance and `except ValueError` still apply."""
22 """Raised when an EXPR cut is internally inconsistent or ill-typed (wrong
23 operand count, an operation undefined for the given object, etc.)."""
26@dataclass(frozen=
True)
29 apply_btag: bool =
False
33@dataclass(frozen=True)
36 max_operands: int |
None
43 """Small recursive-descent parser for EXPR selector expressions."""
52 while pos < len(text):
56 f
"[EventSelectionConfig] EXPR: cannot parse near '{text[pos:]}'")
58 if match.lastgroup !=
"WS":
59 tokens.append((match.lastgroup, match.group()))
60 tokens.append((
"END",
""))
75 f
"[EventSelectionConfig] EXPR: expected {kind}, got '{token[1]}'")
85 return variable, separator, operands, sign, ref_value
88 variable = self.
_expect(
"ID")[1]
92 while self.
_peek()[0]
in (
"COMMA",
"PLUS"):
93 sep =
"," if self.
_advance()[0] ==
"COMMA" else "+"
96 elif sep != separator:
98 "[EventSelectionConfig] EXPR: cannot mix ',' and '+' separators")
101 return variable, separator, operands
106 if self.
_peek()[0] ==
"LB":
108 index = int(self.
_expect(
"NUM")[1])
114 if token[0]
not in (
"LT",
"GT",
"EQ",
"GE",
"LE"):
116 f
"[EventSelectionConfig] EXPR: expected a comparison operator, got '{token[1]}'")
121 if self.
_peek()[0] ==
"MINUS":
124 value = float(self.
_expect(
"NUM")[1])
125 return -value
if negative
else value
130 """ConfigBlock for merging the output of various selection streams"""
133 super(EventSelectionMergerConfig, self).
__init__()
135 self.setBlockName(
'EventSelectionMerger')
136 self.addDependency(
'EventSelection', required=
True)
137 self.addOption(
'noFilter',
False, type=bool,
138 info=
"do not apply an event filter, i.e. setting it to `False` "
139 "removes events not passing the full list of selection cuts.")
142 """Return the instance name for this block"""
150 selections = config.getContainerMeta(
'EventInfo',
'eventSelectionNames',
152 selections = [sel
for sel
in selections
if not sel.startswith(
"pass_SUB")]
154 alg = config.createAlgorithm(
'CP::SaveFilterAlg',
155 'EventSelectionMerger' + selections[0].
split(
"_%SYS%")[0])
156 alg.FilterDescription =
'events passing at least one EventSelection'
157 alg.eventDecisionOutputDecoration =
'ignore_anySelection_%SYS%'
158 alg.selection =
'||'.join([sel +
',as_char' for sel
in selections])
159 alg.noFilter = self.noFilter
160 alg.selectionName =
'pass_anySelection_%SYS%'
161 alg.decorationName =
'ntuplepass_anySelection_%SYS%'
165 """ConfigBlock for interpreting text-based event selections"""
170 "EL_N": (
"electrons",
"NEL",
"electrons"),
171 "MU_N": (
"muons",
"NMU",
"muons"),
172 "JET_N": (
"jets",
"NJET",
"jets"),
173 "PH_N": (
"photons",
"NPH",
"photons"),
174 "TAU_N": (
"taus",
"NTAU",
"tau-jets"),
175 "LJET_N": (
"largeRjets",
"NLJET",
"large-R jets"),
177 _KEYWORD_SPECS =
None
180 super(EventSelectionConfig, self).
__init__()
181 self.setBlockName(
'EventSelection')
182 self.addOption(
'selectionName',
'', type=str,
184 info=
"the name of the event selection, used to uniquely identify "
185 "the `EventSelectionConfig` block.")
186 self.addOption(
'electrons',
"", type=str,
187 info=
"the input electron container, with a possible selection, in "
188 "the format `container` or `container.selection`.")
189 self.addOption(
'muons',
"", type=str,
190 info=
"the input muon container, with a possible selection, in the "
191 "format `container` or `container.selection`.")
192 self.addOption(
'jets',
"", type=str,
193 info=
"the input jet container, with a possible selection, in the "
194 "format `container` or `container.selection`.")
195 self.addOption(
'largeRjets',
"", type=str,
196 info=
"the large-R jet container, with a possible selection, in "
197 "the format `container` or `container.selection`.")
198 self.addOption(
'photons',
"", type=str,
199 info=
"the input photon container, with a possible selection, in "
200 "the format `container` or `container.selection`.")
201 self.addOption(
'taus',
"", type=str,
202 info=
"the input tau-jet container, with a possible selection, in "
203 "the format `container` or `container.selection`.")
204 self.addOption(
'met',
"", type=str,
205 info=
"the input MET container.")
206 self.addOption(
'metTerm',
"Final", type=str,
207 info=
"the MET term to use when computing MET-based quantities.")
208 self.addOption(
'btagDecoration',
"", type=str,
209 info=
"the b-tagging decoration to use when defining b-jets.")
210 self.addOption(
'preselection',
"", type=str,
211 info=
"the event-wise selection flag to start this event selection "
213 self.addOption(
'selectionCuts',
"", type=str,
215 info=
"a single string listing one selection cut per line. "
216 "See [available keywords](https://topcptoolkit.docs.cern.ch/latest/settings/eventselection/#available-keywords).")
217 self.addOption(
'debugMode',
False, type=bool,
218 info=
"whether to create an output branch for every single line "
219 "of the selection cuts. Setting it to `False` only saves the"
221 self.addOption(
'useDressedProperties',
True, type=bool,
222 info=
"whether to use dressed truth electron and truth muon "
223 "kinematics rather than simple 4-vector kinematics.")
230 """Return the instance name for this block"""
234 """Map each keyword to its handler. Dispatch is an exact lookup on the
235 first token, which removes the ordering fragility of token-membership."""
251 "OS": partial(self.
_add_charge, osMode=
True, tag=
"OS"),
252 "SS": partial(self.
_add_charge, osMode=
False, tag=
"SS"),
261 for kw, (attr, tag, _noun)
in self.
_NOBJECT.items():
271 """Machine-readable description of every `selectionCuts` keyword.
274 * `info` (str, required): one-line description of the keyword.
275 * `args` (list): argument descriptors, in token order.
276 * `forms` (list of arg-lists): used instead of `args` for keywords
277 offering several fixed shapes that are not "required + optionals".
278 * `freeText` (True): everything after the keyword is a single
279 expression (EXPR only).
280 * `grammar` (dict): the vocabulary of that expression (EXPR only).
281 * `deprecated` (True): the keyword is deprecated (SAVE only).
283 Argument descriptor fields:
284 * `name` (str), `type` (str), and optionally `optional` (True),
285 `choices` (list), `pattern` (regex str), `signed` (True).
286 * `type` is one of 'str', 'float', 'int', 'sign', 'region', 'flag',
288 - 'sign' is one of `<` `>` `==` `>=` `<=`
289 - 'region' is the `selectionName` of another EventSelection
290 - 'flag' is a literal token, present or absent; the literal
291 is the arg's `name`, matched case-insensitively
292 - 'container' is a container reference, in the format `Name` or
294 * `signed` (True) marks a float that may be negative; every other
295 float goes through `check_float(requirePositive=True)`.
297 Filling rule (implemented by `parseArgs`):
298 1. `flag` arguments are lifted out of the token list first, by
299 case-insensitive match against the arg name.
300 2. Of what remains, required arguments are matched first; optional
301 ones are filled left-to-right with the surplus tokens, skipping an
302 optional whose `pattern` the candidate token does not match.
303 3. With `forms`, the first form whose token count and patterns fit
310 for kw, (_attr, _tag, noun)
in cls.
_NOBJECT.items():
312 'info': f
'Count {noun} above a pT threshold',
314 {
'name':
'sel',
'type':
'str',
'optional':
True},
315 {
'name':
'ptmin',
'type':
'float'},
316 {
'name':
'sign',
'type':
'sign'},
317 {
'name':
'count',
'type':
'int'},
322 'info':
'Count b-tagged jets above the default b-tagging working '
323 'point, or above a custom one given as `tagger:WP`',
325 {
'name':
'sel',
'type':
'str',
'optional':
True,
'pattern':
'^[^:]+$'},
326 {
'name':
'btag',
'type':
'str',
'optional':
True,
'pattern':
'^[^:]+:[^:]+$'},
327 {
'name':
'sign',
'type':
'sign'},
328 {
'name':
'count',
'type':
'int'},
332 'info':
'Count jets ghost-associated to a given particle, e.g. `B`, '
333 'or `B!C` to also veto a second ghost association',
335 {
'name':
'ghost',
'type':
'str',
'pattern':
'^[A-Za-z]+(![A-Za-z]+)?$'},
336 {
'name':
'ptmin',
'type':
'float',
'optional':
True},
337 {
'name':
'sign',
'type':
'sign'},
338 {
'name':
'count',
'type':
'int'},
342 'info':
'Count large-R jets ghost-associated to a given particle, e.g. '
343 '`B`, or `B!C` to also veto a second ghost association',
345 {
'name':
'ghost',
'type':
'str',
'pattern':
'^[A-Za-z]+(![A-Za-z]+)?$'},
346 {
'name':
'ptmin',
'type':
'float',
'optional':
True},
347 {
'name':
'sign',
'type':
'sign'},
348 {
'name':
'count',
'type':
'int'},
352 'info':
'Count large-R jets above a mass threshold',
354 {
'name':
'sel',
'type':
'str',
'optional':
True},
355 {
'name':
'minMass',
'type':
'float'},
356 {
'name':
'sign',
'type':
'sign'},
357 {
'name':
'count',
'type':
'int'},
360 'LJETMASSWINDOW_N': {
361 'info':
'Count large-R jets inside (or, with `veto`, outside) a mass window',
363 {
'name':
'sel',
'type':
'str',
'optional':
True},
364 {
'name':
'lowMass',
'type':
'float'},
365 {
'name':
'highMass',
'type':
'float'},
366 {
'name':
'sign',
'type':
'sign'},
367 {
'name':
'count',
'type':
'int'},
368 {
'name':
'veto',
'type':
'flag',
'optional':
True},
372 'info':
'Count objects of an arbitrary container above a pT threshold',
374 {
'name':
'container',
'type':
'container'},
375 {
'name':
'ptmin',
'type':
'float'},
376 {
'name':
'sign',
'type':
'sign'},
377 {
'name':
'count',
'type':
'int'},
381 'info':
'Count electrons and muons together, above a common or '
382 'per-flavour pT threshold',
385 {
'name':
'ptmin',
'type':
'float'},
386 {
'name':
'sign',
'type':
'sign'},
387 {
'name':
'count',
'type':
'int'},
390 {
'name':
'ptEl',
'type':
'float'},
391 {
'name':
'ptMu',
'type':
'float'},
392 {
'name':
'sign',
'type':
'sign'},
393 {
'name':
'count',
'type':
'int'},
396 {
'name':
'selEl',
'type':
'str'},
397 {
'name':
'selMu',
'type':
'str'},
398 {
'name':
'ptEl',
'type':
'float'},
399 {
'name':
'ptMu',
'type':
'float'},
400 {
'name':
'sign',
'type':
'sign'},
401 {
'name':
'count',
'type':
'int'},
405 'SUM_EL_N_MU_N_TAU_N': {
406 'info':
'Count electrons, muons and tau-jets together, above a common '
407 'or per-flavour pT threshold',
410 {
'name':
'ptmin',
'type':
'float'},
411 {
'name':
'sign',
'type':
'sign'},
412 {
'name':
'count',
'type':
'int'},
415 {
'name':
'ptEl',
'type':
'float'},
416 {
'name':
'ptMu',
'type':
'float'},
417 {
'name':
'ptTau',
'type':
'float'},
418 {
'name':
'sign',
'type':
'sign'},
419 {
'name':
'count',
'type':
'int'},
422 {
'name':
'selEl',
'type':
'str'},
423 {
'name':
'selMu',
'type':
'str'},
424 {
'name':
'selTau',
'type':
'str'},
425 {
'name':
'ptEl',
'type':
'float'},
426 {
'name':
'ptMu',
'type':
'float'},
427 {
'name':
'ptTau',
'type':
'float'},
428 {
'name':
'sign',
'type':
'sign'},
429 {
'name':
'count',
'type':
'int'},
434 'info':
'Cut on the missing transverse energy',
436 {
'name':
'sign',
'type':
'sign'},
437 {
'name':
'refMET',
'type':
'float'},
441 'info':
'Cut on the transverse mass of the leading lepton and MET',
443 {
'name':
'sign',
'type':
'sign'},
444 {
'name':
'refMWT',
'type':
'float'},
448 'info':
'Cut on the sum of the missing transverse energy and the '
451 {
'name':
'sign',
'type':
'sign'},
452 {
'name':
'refMETMWT',
'type':
'float'},
456 'info':
'Cut on the dilepton invariant mass',
458 {
'name':
'sign',
'type':
'sign'},
459 {
'name':
'refMLL',
'type':
'float'},
463 'info':
'Require the dilepton invariant mass inside (or, with `veto`, '
464 'outside) a mass window',
466 {
'name':
'lowMLL',
'type':
'float'},
467 {
'name':
'highMLL',
'type':
'float'},
468 {
'name':
'veto',
'type':
'flag',
'optional':
True},
472 'info':
'Require the opposite-sign same-flavour dilepton invariant mass '
473 'inside (or, with `veto`, outside) a mass window',
475 {
'name':
'lowMll',
'type':
'float'},
476 {
'name':
'highMll',
'type':
'float'},
477 {
'name':
'veto',
'type':
'flag',
'optional':
True},
481 'info':
'Require an opposite-sign lepton pair; without any flag, all '
482 'available lepton flavours are considered',
484 {
'name':
'el',
'type':
'flag',
'optional':
True},
485 {
'name':
'mu',
'type':
'flag',
'optional':
True},
486 {
'name':
'tau',
'type':
'flag',
'optional':
True},
490 'info':
'Require a same-sign lepton pair; without any flag, all '
491 'available lepton flavours are considered',
493 {
'name':
'el',
'type':
'flag',
'optional':
True},
494 {
'name':
'mu',
'type':
'flag',
'optional':
True},
495 {
'name':
'tau',
'type':
'flag',
'optional':
True},
499 'info':
'Import all the cuts of a previously defined event selection',
501 {
'name':
'region',
'type':
'region'},
505 'info':
'Require an existing event-wise decoration to be true',
507 {
'name':
'decoration',
'type':
'str'},
511 'info':
'Require the global trigger matching decision, optionally for '
512 'a given trigger-configuration postfix',
514 {
'name':
'postfix',
'type':
'str',
'optional':
True},
518 'info':
'Cut on the (random) run number',
520 {
'name':
'sign',
'type':
'sign'},
521 {
'name':
'runNumber',
'type':
'int'},
525 'info':
'Cut on an existing EventInfo scalar variable, e.g. a DNN or '
528 {
'name':
'type',
'type':
'str',
'choices': sorted(cls.
_EVENTVAR_TYPES)},
529 {
'name':
'name',
'type':
'str'},
530 {
'name':
'sign',
'type':
'sign'},
531 {
'name':
'value',
'type':
'float',
'signed':
True},
535 'info':
'Cut on a generic object-kinematic expression, e.g. '
536 '`dR(el[0],jet[0]) > 0.4`',
544 'info':
'Deprecated and ignored: the event filter is now emitted '
545 'automatically at the end of every event selection',
555 """Return an independent copy of the keyword specification table."""
560 """Return the set of valid total token counts (leading keyword included)
561 allowed by a keyword specification, or None when the keyword takes free
562 text and no count check applies."""
563 if spec.get(
'freeText'):
565 forms = spec.get(
'forms')
or [spec.get(
'args', [])]
568 min_args = sum(1
for arg
in form
if not arg.get(
'optional'))
570 counts.update(n + 1
for n
in range(min_args, max_args + 1))
574 existing = config.getContainerMeta(
'EventInfo',
'eventSelectionNames', defaultValue=[])
575 config.setContainerMeta(
'EventInfo',
'eventSelectionNames',
576 existing + [f
'pass_{self.selectionName}_%SYS%'], allowOverwrite=
True)
585 if self.selectionCuts
is None:
586 raise ValueError (
"[EventSelectionConfig] You must provide the 'selectionCuts' option to 'EventSelectionConfig': "
587 "a single string where each line represents a different selection cut to apply in order.")
588 for line
in self.selectionCuts.
split(
"\n"):
597 if not text
or text.startswith(
"#"):
600 keyword = text.split()[0]
603 raise ValueError (f
"[EventSelectionConfig] The following selection cut is not recognised! --> {text}")
611 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Check {keyword} in: {text}")
614 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Missing input collection for {collection}")
617 """Validate the leading keyword and the number of arguments, the latter
618 derived from the keyword specification table."""
619 if items[0] != keyword:
622 if validCounts
is not None and len(items)
not in validCounts:
626 """Remove flag tokens from `tokens` and return their boolean values."""
627 tokens = list(tokens)
630 if arg.get(
'type') !=
'flag':
632 values[arg[
'name']] =
False
633 for i, token
in enumerate(tokens):
634 if token.lower() == arg[
'name'].lower():
636 values[arg[
'name']] =
True
638 return tokens, values
641 """Match one keyword argument form, returning values or None."""
643 positional = [arg
for arg
in form
if arg.get(
'type') !=
'flag']
644 nRequired = sum(1
for arg
in positional
if not arg.get(
'optional'))
645 if len(tokens) < nRequired
or len(tokens) > len(positional):
648 budget = len(tokens) - nRequired
650 for arg
in positional:
651 if not arg.get(
'optional'):
652 values[arg[
'name']] = tokens[cursor]
655 pattern = arg.get(
'pattern')
656 if budget > 0
and (pattern
is None or re.fullmatch(pattern, tokens[cursor])):
657 values[arg[
'name']] = tokens[cursor]
661 values[arg[
'name']] =
''
662 return values
if cursor == len(tokens)
else None
665 """Turn the already-split token list `items` (leading keyword included)
666 into a dict of argument name -> value, driven by `keywordSpecs`, which
667 is authoritative for the argument grammar."""
670 if spec.get(
'freeText'):
671 return {
'text':
' '.join(items[1:])}
672 for form
in (spec.get(
'forms')
or [spec.get(
'args', [])]):
674 if values
is not None:
681 if not requirePositive
or value >= 0:
684 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Float {test} is not positive!")
686 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be a float, not {type(test)}!")
690 numeric = float(test)
692 except (TypeError, ValueError):
693 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be an int, not {type(test)}")
695 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be an int, not a float!")
696 if requirePositive
and value < 0:
697 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Int {test} is not positive!")
701 if not isinstance(test, str):
702 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be a string, not a number!")
717 raise KeyError (f
"[EventSelectionConfig] Misconfiguration! {test} should be one of {list(mapping.keys())}")
720 test = test.split(
":")
722 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be provided as 'btagger:btagWP'")
728 values = test.split(
"!")
730 "B":
"GhostBHadronsFinalCount",
731 "C":
"GhostCHadronsFinalCount",
732 "T":
"GhostTQuarksFinalCount",
733 "W":
"GhostWBosonsCount",
734 "Z":
"GhostZBosonsCount",
735 "H":
"GhostHBosonsCount",
736 "TAU":
"GhostTausFinalCount"
738 return [ghost_map.get(value.upper(), value)
for value
in values]
748 self.
cutflow.append( decoration )
749 if algorithm
is not None:
750 algorithm.decorationName = f
'{decoration},as_char'
753 config.addOutputVar(
'EventInfo', decoration, decoration.split(
"_%SYS%")[0])
759 config.addSelection(
'EventInfo',
'', decoration)
764 decoration = decoration.split(
"&&")
765 decoration = [sub +
',as_char' if ',as_char' not in sub
else sub
for sub
in decoration]
766 return '&&'.join(decoration)
770 return oldSelection +
"&&" + config.getFullSelection(container, newSelection)
772 return config.getFullSelection(container, newSelection)
779 """Require at least one of the named input options to be configured."""
780 if not any(getattr(self, name)
for name
in names):
784 """Apply the common event-preselection and output decoration."""
789 """Configure reco/truth lepton handles from explicit metadata."""
790 for spec, reco, truth
in leptons:
793 self.
_maybe_dressed(alg, *(spec
for spec, _reco, _truth
in leptons))
796 """Enable dressed kinematics when any of the given electron/muon
797 containers is a truth container. Dressed kinematics only exist for
798 truth electrons and muons, so only those specs should be passed here."""
799 if any(spec
and (
"Particle" in spec
or "Truth" in spec)
for spec
in specs):
800 alg.useDressedProperties = self.useDressedProperties
803 """Assign (name, selection) to the reco or truth handles of `alg`
804 depending on whether `spec` points to a truth container.
805 `reco`/`truth` are (nameAttr, selectionAttr) pairs."""
806 name, sel = config.readNameAndSelection(spec)
807 nameAttr, selAttr = truth
if (
"Particle" in spec
or "Truth" in spec)
else reco
808 setattr(alg, nameAttr, name)
809 setattr(alg, selAttr, sel)
816 """Generic builder for the N-object pT selectors (EL_N, MU_N, JET_N,
817 PH_N, TAU_N, LJET_N): identical except for the source container, which
818 is always required since the cut acts on it."""
820 spec = getattr(self, attr)
824 thisalg = f
'{self.selectionName}_{tag}_{self.step}'
825 alg = config.createAlgorithm(
'CP::NObjectPtSelectorAlg', thisalg)
826 alg.particles, alg.objectSelection = config.readNameAndSelection(spec)
827 if attr
in (
"electrons",
"muons"):
831 config, spec.split(
".")[0], alg.objectSelection,
835 alg.count = self.
check_int(args[
'count'])
840 args = self.
parseArgs(
"IMPORT", text.split())
845 self.
currentDecoration = f
'{self.currentDecoration},as_char&&pass_{region}_%SYS%'
847 imported_cuts = [cut
for cut
in config.getSelectionCutFlow(
'EventInfo',
'')
if cut.startswith(region)]
851 args = self.
parseArgs(
"JET_N_BTAG", text.split())
853 thisalg = f
'{self.selectionName}_NBJET_{self.step}'
854 alg = config.createAlgorithm(
'CP::NObjectPtSelectorAlg', thisalg)
855 particles, selection = config.readNameAndSelection(self.jets)
856 alg.particles = particles
857 alg.objectSelection = f
'{selection}&&{self.btagDecoration},as_char' if selection
else f
'{self.btagDecoration},as_char'
860 customBtag = f
'ftag_select_{btagger}_{btagWP}'
861 alg.objectSelection = f
'{selection}&&{customBtag},as_char' if selection
else f
'{customBtag},as_char'
866 alg.count = self.
check_int(args[
'count'])
870 args = self.
parseArgs(
"SUM_EL_N_MU_N", text.split())
872 thisalg = f
'{self.selectionName}_SUMNELNMU_{self.step}'
873 alg = config.createAlgorithm(
'CP::SumNLeptonPtSelectorAlg', thisalg)
874 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
875 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
877 if args.get(
'ptmin')
is not None:
882 if args.get(
'selEl'):
885 if args.get(
'selMu'):
891 alg.count = self.
check_int(args[
'count'])
895 args = self.
parseArgs(
"SUM_EL_N_MU_N_TAU_N", text.split())
896 self.
_require_inputs(
"electrons",
"muons",
"taus", message=
"electrons, muons or taus")
897 thisalg = f
'{self.selectionName}_SUMNLEPTONS_{self.step}'
898 alg = config.createAlgorithm(
'CP::SumNLeptonPtSelectorAlg', thisalg)
899 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
900 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
901 alg.taus, alg.tauSelection = config.readNameAndSelection(self.
taus)
903 if args.get(
'ptmin')
is not None:
909 if args.get(
'selEl'):
912 if args.get(
'selMu'):
915 if args.get(
'selTau'):
922 alg.count = self.
check_int(args[
'count'])
926 args = self.
parseArgs(
"LJETMASS_N", text.split())
927 thisalg = f
'{self.selectionName}_NLJETMASS_{self.step}'
928 alg = config.createAlgorithm(
'CP::NObjectMassSelectorAlg', thisalg)
929 alg.particles, alg.objectSelection = config.readNameAndSelection(self.largeRjets)
932 config, self.largeRjets.
split(
".")[0], alg.objectSelection,
936 alg.count = self.
check_int(args[
'count'])
940 args = self.
parseArgs(
"LJETMASSWINDOW_N", text.split())
941 thisalg = f
'{self.selectionName}_NLJETMASSWINDOW_{self.step}'
942 alg = config.createAlgorithm(
'CP::NLargeRJetMassWindowSelectorAlg', thisalg)
943 alg.ljets, alg.ljetSelection = config.readNameAndSelection(self.largeRjets)
950 alg.count = self.
check_int(args[
'count'])
951 alg.vetoMode = args[
'veto']
955 args = self.
parseArgs(
"JET_N_GHOST", text.split())
956 thisalg = f
'{self.selectionName}_NJETGHOST_{self.step}'
957 alg = config.createAlgorithm(
'CP::JetNGhostSelectorAlg', thisalg)
958 alg.jets, alg.jetSelection = config.readNameAndSelection(self.jets)
960 alg.ghost = ghosts[0]
966 alg.count = self.
check_int(args[
'count'])
970 args = self.
parseArgs(
"LJET_N_GHOST", text.split())
971 thisalg = f
'{self.selectionName}_NLJETGHOST_{self.step}'
972 alg = config.createAlgorithm(
'CP::JetNGhostSelectorAlg', thisalg)
973 alg.jets, alg.jetSelection = config.readNameAndSelection(self.largeRjets)
975 alg.ghost = ghosts[0]
981 alg.count = self.
check_int(args[
'count'])
985 args = self.
parseArgs(
"OBJ_N", text.split())
986 thisalg = f
'{self.selectionName}_NOBJ_{self.step}'
987 alg = config.createAlgorithm(
'CP::NObjectPtSelectorAlg', thisalg)
988 alg.particles, alg.objectSelection = config.readNameAndSelection(self.
check_string(args[
'container']))
991 alg.count = self.
check_int(args[
'count'])
995 args = self.
parseArgs(
"MET", text.split())
997 thisalg = f
'{self.selectionName}_MET_{self.step}'
998 alg = config.createAlgorithm(
'CP::MissingETSelectorAlg', thisalg)
999 alg.met = config.readName(self.
met)
1000 alg.metTerm = self.metTerm
1006 args = self.
parseArgs(
"MWT", text.split())
1008 thisalg = f
'{self.selectionName}_MWT_{self.step}'
1009 alg = config.createAlgorithm(
'CP::TransverseMassSelectorAlg', thisalg)
1010 alg.met = config.readName(self.
met)
1011 alg.metTerm = self.metTerm
1012 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
1013 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
1020 args = self.
parseArgs(
"MET+MWT", text.split())
1023 thisalg = f
'{self.selectionName}_METMWT_{self.step}'
1024 alg = config.createAlgorithm(
'CP::MissingETPlusTransverseMassSelectorAlg', thisalg)
1025 alg.met = config.readName(self.
met)
1026 alg.metTerm = self.metTerm
1027 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
1028 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
1031 alg.refMETMWT = self.
check_float(args[
'refMETMWT'])
1035 args = self.
parseArgs(
"MLL", text.split())
1037 thisalg = f
'{self.selectionName}_MLL_{self.step}'
1038 alg = config.createAlgorithm(
'CP::DileptonInvariantMassSelectorAlg', thisalg)
1040 (self.
electrons, (
'electrons',
'electronSelection'),
1041 (
'truthElectrons',
'truthElectronSelection')),
1042 (self.
muons, (
'muons',
'muonSelection'),
1043 (
'truthMuons',
'truthMuonSelection')),
1050 args = self.
parseArgs(
"MLLWINDOW", text.split())
1052 thisalg = f
'{self.selectionName}_MLLWINDOW_{self.step}'
1053 alg = config.createAlgorithm(
'CP::DileptonInvariantMassWindowSelectorAlg', thisalg)
1055 (self.
electrons, (
'electrons',
'electronSelection'),
1056 (
'truthElectrons',
'truthElectronSelection')),
1057 (self.
muons, (
'muons',
'muonSelection'),
1058 (
'truthMuons',
'truthMuonSelection')),
1062 alg.vetoMode = args[
'veto']
1066 """Builder shared by OS and SS: same algorithm, opposite charge mode."""
1067 args = self.
parseArgs(tag, text.split())
1069 thisalg = f
'{self.selectionName}_{tag}_{self.step}'
1070 alg = config.createAlgorithm(
'CP::ChargeSelectorAlg', thisalg)
1071 allLeptons =
not (args[
'el']
or args[
'mu']
or args[
'tau'])
1072 if self.
electrons and (allLeptons
or args[
'el']):
1074 (
'electrons',
'electronSelection'),
1075 (
'truthElectrons',
'truthElectronSelection'))
1076 if self.
muons and (allLeptons
or args[
'mu']):
1078 (
'muons',
'muonSelection'),
1079 (
'truthMuons',
'truthMuonSelection'))
1080 if self.
taus and (allLeptons
or args[
'tau']):
1082 (
'taus',
'tauSelection'),
1083 (
'truthTaus',
'truthTauSelection'))
1088 args = self.
parseArgs(
"MLL_OSSF", text.split())
1090 thisalg = f
'{self.selectionName}_MLL_OSSF_{self.step}'
1091 alg = config.createAlgorithm(
'CP::DileptonOSSFInvariantMassWindowSelectorAlg', thisalg)
1093 (self.
electrons, (
'electrons',
'electronSelection'),
1094 (
'truthElectrons',
'truthElectronSelection')),
1095 (self.
muons, (
'muons',
'muonSelection'),
1096 (
'truthMuons',
'truthMuonSelection')),
1100 alg.vetoMode = args[
'veto']
1104 args = self.
parseArgs(
"EVENTFLAG", text.split())
1105 existingDecoration = self.
check_string(args[
'decoration'])
1109 args = self.
parseArgs(
"GLOBALTRIGMATCH", text.split())
1111 self.
setDecorationName(
None, config, f
"globalTriggerMatch{postfix}_%SYS%,as_char")
1114 args = self.
parseArgs(
"RUN_NUMBER", text.split())
1115 thisalg = f
'{self.selectionName}_RUN_NUMBER_{self.step}'
1116 alg = config.createAlgorithm(
'CP::RunNumberSelectorAlg', thisalg)
1118 alg.runNumber = self.
check_int(args[
'runNumber'])
1119 alg.useRandomRunNumber = config.dataType()
is not DataType.Data
1150 _EXPR_TOKEN_RE = re.compile(
r"""
1151 (?P<NUM>\d+\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+|\d+)
1152 | (?P<GE>>=) | (?P<LE><=) | (?P<EQ>==) | (?P<LT><) | (?P<GT>>)
1153 | (?P<ID>[A-Za-z_][A-Za-z0-9_]*)
1154 | (?P<LP>\() | (?P<RP>\)) | (?P<LB>\[) | (?P<RB>\])
1155 | (?P<COMMA>,) | (?P<PLUS>\+) | (?P<MINUS>-)
1169 f
"[EventSelectionConfig] EXPR: variable '{variable}' is not available. "
1170 "Please request it from the EventSelectionAlgorithms developers.")
1172 minN = var_spec.min_operands
1173 maxN = var_spec.max_operands
1174 sep = var_spec.separator
1175 metOk = var_spec.met_ok
1177 if n < minN
or (maxN
is not None and n > maxN):
1178 expected = f
"{minN}" if maxN == minN
else (f
"{minN}+" if maxN
is None else f
"{minN}-{maxN}")
1180 f
"[EventSelectionConfig] EXPR: '{variable}' takes {expected} operand(s), got {n}")
1181 if n > 1
and separator != sep:
1182 want = {
",":
"','",
"+":
"'+'"}.
get(sep, str(sep))
1184 f
"[EventSelectionConfig] EXPR: '{variable}' operands must be separated by {want}")
1185 for coll, index
in operands:
1188 f
"[EventSelectionConfig] EXPR: collection '{coll}' is not available. "
1189 "Please request it from the EventSelectionAlgorithms developers.")
1191 isMET = coll_spec.is_met
1195 f
"[EventSelectionConfig] EXPR: 'met' is not valid for '{variable}'")
1196 if index
is not None:
1198 "[EventSelectionConfig] EXPR: 'met' cannot be indexed")
1199 if separator ==
"+":
1201 "[EventSelectionConfig] EXPR: 'met' cannot be combined in a sum")
1204 f
"[EventSelectionConfig] EXPR: '{coll}' must be indexed, e.g. {coll}[0]")
1207 body = text[len(
"EXPR"):].
strip()
1213 thisalg = f
'{self.selectionName}_EXPR_{self.step}'
1214 alg = config.createAlgorithm(
'CP::ObjectKinematicSelectorAlg', thisalg)
1215 alg.variable = variable
1217 alg.refValue = refValue
1219 operandKinds, collections, selections, indices = [], [], [], []
1221 for coll, index
in operands:
1223 opt = coll_spec.option
1224 applyBtag = coll_spec.apply_btag
1225 isMET = coll_spec.is_met
1226 container = getattr(self, opt)
1230 operandKinds.append(
"MET")
1233 operandKinds.append(
"PARTICLE")
1234 name, selection = config.readNameAndSelection(container)
1238 selection = (f
'{selection}&&{self.btagDecoration},as_char'
1239 if selection
else f
'{self.btagDecoration},as_char')
1240 collections.append(name)
1241 selections.append(selection)
1242 indices.append(index)
1244 alg.operandKinds = operandKinds
1245 alg.collections = collections
1246 alg.selections = selections
1247 alg.indices = indices
1251 alg.met = config.readName(self.
met)
1252 alg.metTerm = self.metTerm
1258 _EVENTVAR_TYPES = {
"float":
"floatVariable",
"int":
"intVariable",
"double":
"doubleVariable"}
1262 args = self.
parseArgs(
"EVENTVAR", text.split())
1265 self.
raise_misconfig(text, f
"value type (one of {sorted(self._EVENTVAR_TYPES)})")
1267 thisalg = f
'{self.selectionName}_EVENTVAR_{self.step}'
1268 alg = config.createAlgorithm(
'CP::EventScalarSelectorAlg', thisalg)
1272 alg.refValue = self.
check_float(args[
'value'], requirePositive=
False)
1281 "[EventSelectionConfig] The 'SAVE' keyword is deprecated: the event "
1282 "filter is now created automatically at the end of each EventSelection "
1283 f
"block. Please remove the 'SAVE' line from selection '{self.selectionName}'.",
1284 category=ConfigDeprecationWarning, stacklevel=2)
1287 """Create the SaveFilterAlg that turns the accumulated event selection
1288 into a named, persisted selection (and ntuple branch). Called once per
1289 block, automatically at the end of makeAlgs."""
1290 thisalg = f
'{self.selectionName}_SAVE'
1291 alg = config.createAlgorithm(
'CP::SaveFilterAlg', thisalg)
1292 alg.FilterDescription = f
'events passing < {self.selectionName} >'
1293 alg.eventDecisionOutputDecoration = f
'ignore_{self.selectionName}_%SYS%'
1296 alg.selectionName = f
'pass_{self.selectionName}_%SYS%,as_char'
1297 alg.decorationName = f
'ntuplepass_{self.selectionName}_%SYS%'
1298 config.addOutputVar(
'EventInfo', f
'ntuplepass_{self.selectionName}_%SYS%', f
'pass_{self.selectionName}')
1304 seq.append(EventCutFlowBlock())
std::vector< std::string > tokenize(std::string_view the_str, std::string_view delimiters)
Splits the string into smaller substrings.
setDecorationName(self, algorithm, config, decoration)
add_SUMNELNMU_selector(self, text, config)
raise_missinginput(self, collection)
extendObjectSelection(self, config, container, oldSelection, newSelection)
_extract_flags(self, form, tokens)
add_RUNNUMBER(self, text, config)
add_MLL_selector(self, text, config)
check_int(self, test, requirePositive=True)
add_METMWT_selector(self, text, config)
add_EVENTVAR_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)
_check_args(self, items, keyword)
_require_inputs(self, *names, message=None)
_configure_leptons(self, alg, config, leptons)
add_IMPORT(self, text, config)
_add_nobject(self, text, config, *, attr, tag)
add_GLOBALTRIGMATCH(self, text, config)
parseArgs(self, keyword, items)
raise_misconfig(self, text, keyword)
_expr_parse(self, tokens)
_finish_selector(self, alg, config, name)
add_NBJET_selector(self, text, config)
_expr_validate(self, variable, separator, operands)
checkDecorationName(self, decoration)
add_NLJETMASS_selector(self, text, config)
check_float(self, test, requirePositive=True)
interpret(self, text, cfg)
_match_form(self, form, tokens)
_maybe_dressed(self, alg, *specs)
_expr_tokenize(self, text)
add_MET_selector(self, text, config)
check_btagging(self, test)
add_EXPR_selector(self, text, config)
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=":")
bool match(std::string s1, std::string s2)
match the individual directories of two strings
void handler(int sig)
signal handler