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 meta={
'role':
'region'})
187 self.addOption(
'electrons',
"", type=str,
188 info=
"the input electron container, with a possible selection, in "
189 "the format `container` or `container.selection`.",
190 meta={
'role':
'containerRef'})
191 self.addOption(
'muons',
"", type=str,
192 info=
"the input muon container, with a possible selection, in the "
193 "format `container` or `container.selection`.",
194 meta={
'role':
'containerRef'})
195 self.addOption(
'jets',
"", type=str,
196 info=
"the input jet container, with a possible selection, in the "
197 "format `container` or `container.selection`.",
198 meta={
'role':
'containerRef'})
199 self.addOption(
'largeRjets',
"", type=str,
200 info=
"the large-R jet container, with a possible selection, in "
201 "the format `container` or `container.selection`.",
202 meta={
'role':
'containerRef'})
203 self.addOption(
'photons',
"", type=str,
204 info=
"the input photon container, with a possible selection, in "
205 "the format `container` or `container.selection`.",
206 meta={
'role':
'containerRef'})
207 self.addOption(
'taus',
"", type=str,
208 info=
"the input tau-jet container, with a possible selection, in "
209 "the format `container` or `container.selection`.",
210 meta={
'role':
'containerRef'})
211 self.addOption(
'met',
"", type=str,
212 info=
"the input MET container.",
213 meta={
'role':
'containerRef'})
214 self.addOption(
'metTerm',
"Final", type=str,
215 info=
"the MET term to use when computing MET-based quantities.")
216 self.addOption(
'btagDecoration',
"", type=str,
217 info=
"the b-tagging decoration to use when defining b-jets.")
218 self.addOption(
'preselection',
"", type=str,
219 info=
"the event-wise selection flag to start this event selection "
221 meta={
'role':
'region'})
222 self.addOption(
'selectionCuts',
"", type=str,
224 info=
"a single string listing one selection cut per line. "
225 "See [available keywords](https://topcptoolkit.docs.cern.ch/latest/settings/eventselection/#available-keywords).")
226 self.addOption(
'debugMode',
False, type=bool,
227 info=
"whether to create an output branch for every single line "
228 "of the selection cuts. Setting it to `False` only saves the"
230 self.addOption(
'useDressedProperties',
True, type=bool,
231 info=
"whether to use dressed truth electron and truth muon "
232 "kinematics rather than simple 4-vector kinematics.")
239 """Return the instance name for this block"""
243 """Map each keyword to its handler. Dispatch is an exact lookup on the
244 first token, which removes the ordering fragility of token-membership."""
260 "OS": partial(self.
_add_charge, osMode=
True, tag=
"OS"),
261 "SS": partial(self.
_add_charge, osMode=
False, tag=
"SS"),
270 for kw, (attr, tag, _noun)
in self.
_NOBJECT.items():
280 """Machine-readable description of every `selectionCuts` keyword.
283 * `info` (str, required): one-line description of the keyword.
284 * `args` (list): argument descriptors, in token order.
285 * `forms` (list of arg-lists): used instead of `args` for keywords
286 offering several fixed shapes that are not "required + optionals".
287 * `freeText` (True): everything after the keyword is a single
288 expression (EXPR only).
289 * `grammar` (dict): the vocabulary of that expression (EXPR only).
290 * `deprecated` (True): the keyword is deprecated (SAVE only).
292 Argument descriptor fields:
293 * `name` (str), `type` (str), and optionally `optional` (True),
294 `choices` (list), `pattern` (regex str), `signed` (True).
295 * `type` is one of 'str', 'float', 'int', 'sign', 'region', 'flag',
297 - 'sign' is one of `<` `>` `==` `>=` `<=`
298 - 'region' is the `selectionName` of another EventSelection
299 - 'flag' is a literal token, present or absent; the literal
300 is the arg's `name`, matched case-insensitively
301 - 'container' is a container reference, in the format `Name` or
303 * `signed` (True) marks a float that may be negative; every other
304 float goes through `check_float(requirePositive=True)`.
306 Filling rule (implemented by `parseArgs`):
307 1. `flag` arguments are lifted out of the token list first, by
308 case-insensitive match against the arg name.
309 2. Of what remains, required arguments are matched first; optional
310 ones are filled left-to-right with the surplus tokens, skipping an
311 optional whose `pattern` the candidate token does not match.
312 3. With `forms`, the first form whose token count and patterns fit
319 for kw, (_attr, _tag, noun)
in cls.
_NOBJECT.items():
321 'info': f
'Count {noun} above a pT threshold',
323 {
'name':
'sel',
'type':
'str',
'optional':
True},
324 {
'name':
'ptmin',
'type':
'float'},
325 {
'name':
'sign',
'type':
'sign'},
326 {
'name':
'count',
'type':
'int'},
331 'info':
'Count b-tagged jets above the default b-tagging working '
332 'point, or above a custom one given as `tagger:WP`',
334 {
'name':
'sel',
'type':
'str',
'optional':
True,
'pattern':
'^[^:]+$'},
335 {
'name':
'btag',
'type':
'str',
'optional':
True,
'pattern':
'^[^:]+:[^:]+$'},
336 {
'name':
'sign',
'type':
'sign'},
337 {
'name':
'count',
'type':
'int'},
341 'info':
'Count jets ghost-associated to a given particle, e.g. `B`, '
342 'or `B!C` to also veto a second ghost association',
344 {
'name':
'ghost',
'type':
'str',
'pattern':
'^[A-Za-z]+(![A-Za-z]+)?$'},
345 {
'name':
'ptmin',
'type':
'float',
'optional':
True},
346 {
'name':
'sign',
'type':
'sign'},
347 {
'name':
'count',
'type':
'int'},
351 'info':
'Count large-R jets ghost-associated to a given particle, e.g. '
352 '`B`, or `B!C` to also veto a second ghost association',
354 {
'name':
'ghost',
'type':
'str',
'pattern':
'^[A-Za-z]+(![A-Za-z]+)?$'},
355 {
'name':
'ptmin',
'type':
'float',
'optional':
True},
356 {
'name':
'sign',
'type':
'sign'},
357 {
'name':
'count',
'type':
'int'},
361 'info':
'Count large-R jets above a mass threshold',
363 {
'name':
'sel',
'type':
'str',
'optional':
True},
364 {
'name':
'minMass',
'type':
'float'},
365 {
'name':
'sign',
'type':
'sign'},
366 {
'name':
'count',
'type':
'int'},
369 'LJETMASSWINDOW_N': {
370 'info':
'Count large-R jets inside (or, with `veto`, outside) a mass window',
372 {
'name':
'sel',
'type':
'str',
'optional':
True},
373 {
'name':
'lowMass',
'type':
'float'},
374 {
'name':
'highMass',
'type':
'float'},
375 {
'name':
'sign',
'type':
'sign'},
376 {
'name':
'count',
'type':
'int'},
377 {
'name':
'veto',
'type':
'flag',
'optional':
True},
381 'info':
'Count objects of an arbitrary container above a pT threshold',
383 {
'name':
'container',
'type':
'container'},
384 {
'name':
'ptmin',
'type':
'float'},
385 {
'name':
'sign',
'type':
'sign'},
386 {
'name':
'count',
'type':
'int'},
390 'info':
'Count electrons and muons together, above a common or '
391 'per-flavour pT threshold',
394 {
'name':
'ptmin',
'type':
'float'},
395 {
'name':
'sign',
'type':
'sign'},
396 {
'name':
'count',
'type':
'int'},
399 {
'name':
'ptEl',
'type':
'float'},
400 {
'name':
'ptMu',
'type':
'float'},
401 {
'name':
'sign',
'type':
'sign'},
402 {
'name':
'count',
'type':
'int'},
405 {
'name':
'selEl',
'type':
'str'},
406 {
'name':
'selMu',
'type':
'str'},
407 {
'name':
'ptEl',
'type':
'float'},
408 {
'name':
'ptMu',
'type':
'float'},
409 {
'name':
'sign',
'type':
'sign'},
410 {
'name':
'count',
'type':
'int'},
414 'SUM_EL_N_MU_N_TAU_N': {
415 'info':
'Count electrons, muons and tau-jets together, above a common '
416 'or per-flavour pT threshold',
419 {
'name':
'ptmin',
'type':
'float'},
420 {
'name':
'sign',
'type':
'sign'},
421 {
'name':
'count',
'type':
'int'},
424 {
'name':
'ptEl',
'type':
'float'},
425 {
'name':
'ptMu',
'type':
'float'},
426 {
'name':
'ptTau',
'type':
'float'},
427 {
'name':
'sign',
'type':
'sign'},
428 {
'name':
'count',
'type':
'int'},
431 {
'name':
'selEl',
'type':
'str'},
432 {
'name':
'selMu',
'type':
'str'},
433 {
'name':
'selTau',
'type':
'str'},
434 {
'name':
'ptEl',
'type':
'float'},
435 {
'name':
'ptMu',
'type':
'float'},
436 {
'name':
'ptTau',
'type':
'float'},
437 {
'name':
'sign',
'type':
'sign'},
438 {
'name':
'count',
'type':
'int'},
443 'info':
'Cut on the missing transverse energy',
445 {
'name':
'sign',
'type':
'sign'},
446 {
'name':
'refMET',
'type':
'float'},
450 'info':
'Cut on the transverse mass of the leading lepton and MET',
452 {
'name':
'sign',
'type':
'sign'},
453 {
'name':
'refMWT',
'type':
'float'},
457 'info':
'Cut on the sum of the missing transverse energy and the '
460 {
'name':
'sign',
'type':
'sign'},
461 {
'name':
'refMETMWT',
'type':
'float'},
465 'info':
'Cut on the light dilepton invariant mass (optionally including taus with `include_taus`)',
467 {
'name':
'sign',
'type':
'sign'},
468 {
'name':
'refMLL',
'type':
'float'},
469 {
'name':
'include_taus',
'type':
'flag',
'optional':
True},
473 'info':
'Require the light dilepton invariant mass inside (or, with `veto`, '
474 'outside) a mass window (optionally including taus with `include_taus`)',
476 {
'name':
'lowMLL',
'type':
'float'},
477 {
'name':
'highMLL',
'type':
'float'},
478 {
'name':
'veto',
'type':
'flag',
'optional':
True},
479 {
'name':
'include_taus',
'type':
'flag',
'optional':
True},
483 'info':
'Require the opposite-sign same-flavour light dilepton invariant mass '
484 'inside (or, with `veto`, outside) a mass window (optionally including taus with `include_taus`)',
486 {
'name':
'lowMll',
'type':
'float'},
487 {
'name':
'highMll',
'type':
'float'},
488 {
'name':
'veto',
'type':
'flag',
'optional':
True},
489 {
'name':
'include_taus',
'type':
'flag',
'optional':
True},
493 'info':
'Require an opposite-sign lepton pair; without any flag, all '
494 'available lepton flavours are considered',
496 {
'name':
'el',
'type':
'flag',
'optional':
True},
497 {
'name':
'mu',
'type':
'flag',
'optional':
True},
498 {
'name':
'tau',
'type':
'flag',
'optional':
True},
502 'info':
'Require a same-sign lepton pair; without any flag, all '
503 'available lepton flavours are considered',
505 {
'name':
'el',
'type':
'flag',
'optional':
True},
506 {
'name':
'mu',
'type':
'flag',
'optional':
True},
507 {
'name':
'tau',
'type':
'flag',
'optional':
True},
511 'info':
'Import all the cuts of a previously defined event selection',
513 {
'name':
'region',
'type':
'region'},
517 'info':
'Require an existing event-wise decoration to be true',
519 {
'name':
'decoration',
'type':
'str'},
523 'info':
'Require the global trigger matching decision, optionally for '
524 'a given trigger-configuration postfix',
526 {
'name':
'postfix',
'type':
'str',
'optional':
True},
530 'info':
'Cut on the (random) run number',
532 {
'name':
'sign',
'type':
'sign'},
533 {
'name':
'runNumber',
'type':
'int'},
537 'info':
'Cut on an existing EventInfo scalar variable, e.g. a DNN or '
540 {
'name':
'type',
'type':
'str',
'choices': sorted(cls.
_EVENTVAR_TYPES)},
541 {
'name':
'name',
'type':
'str'},
542 {
'name':
'sign',
'type':
'sign'},
543 {
'name':
'value',
'type':
'float',
'signed':
True},
547 'info':
'Cut on a generic object-kinematic expression, e.g. '
548 '`dR(el[0],jet[0]) > 0.4`',
556 'info':
'Deprecated and ignored: the event filter is now emitted '
557 'automatically at the end of every event selection',
567 """Return an independent copy of the keyword specification table."""
572 """Return the set of valid total token counts (leading keyword included)
573 allowed by a keyword specification, or None when the keyword takes free
574 text and no count check applies."""
575 if spec.get(
'freeText'):
577 forms = spec.get(
'forms')
or [spec.get(
'args', [])]
580 min_args = sum(1
for arg
in form
if not arg.get(
'optional'))
582 counts.update(n + 1
for n
in range(min_args, max_args + 1))
586 existing = config.getContainerMeta(
'EventInfo',
'eventSelectionNames', defaultValue=[])
587 config.setContainerMeta(
'EventInfo',
'eventSelectionNames',
588 existing + [f
'pass_{self.selectionName}_%SYS%'], allowOverwrite=
True)
597 if self.selectionCuts
is None:
598 raise ValueError (
"[EventSelectionConfig] You must provide the 'selectionCuts' option to 'EventSelectionConfig': "
599 "a single string where each line represents a different selection cut to apply in order.")
600 for line
in self.selectionCuts.
split(
"\n"):
609 if not text
or text.startswith(
"#"):
612 keyword = text.split()[0]
615 raise ValueError (f
"[EventSelectionConfig] The following selection cut is not recognised! --> {text}")
623 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Check {keyword} in: {text}")
626 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Missing input collection for {collection}")
629 """Validate the leading keyword and the number of arguments, the latter
630 derived from the keyword specification table."""
631 if items[0] != keyword:
634 if validCounts
is not None and len(items)
not in validCounts:
638 """Remove flag tokens from `tokens` and return their boolean values."""
639 tokens = list(tokens)
642 if arg.get(
'type') !=
'flag':
644 values[arg[
'name']] =
False
645 for i, token
in enumerate(tokens):
646 if token.lower() == arg[
'name'].lower():
648 values[arg[
'name']] =
True
650 return tokens, values
653 """Match one keyword argument form, returning values or None."""
655 positional = [arg
for arg
in form
if arg.get(
'type') !=
'flag']
656 nRequired = sum(1
for arg
in positional
if not arg.get(
'optional'))
657 if len(tokens) < nRequired
or len(tokens) > len(positional):
660 budget = len(tokens) - nRequired
662 for arg
in positional:
663 if not arg.get(
'optional'):
664 values[arg[
'name']] = tokens[cursor]
667 pattern = arg.get(
'pattern')
668 if budget > 0
and (pattern
is None or re.fullmatch(pattern, tokens[cursor])):
669 values[arg[
'name']] = tokens[cursor]
673 values[arg[
'name']] =
''
674 return values
if cursor == len(tokens)
else None
677 """Turn the already-split token list `items` (leading keyword included)
678 into a dict of argument name -> value, driven by `keywordSpecs`, which
679 is authoritative for the argument grammar."""
682 if spec.get(
'freeText'):
683 return {
'text':
' '.join(items[1:])}
684 for form
in (spec.get(
'forms')
or [spec.get(
'args', [])]):
686 if values
is not None:
693 if not requirePositive
or value >= 0:
696 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Float {test} is not positive!")
698 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be a float, not {type(test)}!")
702 numeric = float(test)
704 except (TypeError, ValueError):
705 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be an int, not {type(test)}")
707 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be an int, not a float!")
708 if requirePositive
and value < 0:
709 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! Int {test} is not positive!")
713 if not isinstance(test, str):
714 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be a string, not a number!")
729 raise KeyError (f
"[EventSelectionConfig] Misconfiguration! {test} should be one of {list(mapping.keys())}")
732 test = test.split(
":")
734 raise ValueError (f
"[EventSelectionConfig] Misconfiguration! {test} should be provided as 'btagger:btagWP'")
740 values = test.split(
"!")
742 "B":
"GhostBHadronsFinalCount",
743 "C":
"GhostCHadronsFinalCount",
744 "T":
"GhostTQuarksFinalCount",
745 "W":
"GhostWBosonsCount",
746 "Z":
"GhostZBosonsCount",
747 "H":
"GhostHBosonsCount",
748 "TAU":
"GhostTausFinalCount"
750 return [ghost_map.get(value.upper(), value)
for value
in values]
760 self.
cutflow.append( decoration )
761 if algorithm
is not None:
762 algorithm.decorationName = f
'{decoration},as_char'
765 config.addOutputVar(
'EventInfo', decoration, decoration.split(
"_%SYS%")[0])
771 config.addSelection(
'EventInfo',
'', decoration)
776 decoration = decoration.split(
"&&")
777 decoration = [sub +
',as_char' if ',as_char' not in sub
else sub
for sub
in decoration]
778 return '&&'.join(decoration)
782 return oldSelection +
"&&" + config.getFullSelection(container, newSelection)
784 return config.getFullSelection(container, newSelection)
791 """Require at least one of the named input options to be configured."""
792 if not any(getattr(self, name)
for name
in names):
796 """Apply the common event-preselection and output decoration."""
801 """Configure reco/truth lepton handles from explicit metadata."""
802 for spec, reco, truth
in leptons:
805 self.
_maybe_dressed(alg, *(spec
for spec, _reco, _truth
in leptons))
808 """Enable dressed kinematics when any of the given electron/muon
809 containers is a truth container. Dressed kinematics only exist for
810 truth electrons and muons, so only those specs should be passed here."""
811 if any(spec
and (
"Particle" in spec
or "Truth" in spec)
for spec
in specs):
812 alg.useDressedProperties = self.useDressedProperties
815 """Assign (name, selection) to the reco or truth handles of `alg`
816 depending on whether `spec` points to a truth container.
817 `reco`/`truth` are (nameAttr, selectionAttr) pairs."""
818 name, sel = config.readNameAndSelection(spec)
819 nameAttr, selAttr = truth
if (
"Particle" in spec
or "Truth" in spec)
else reco
820 setattr(alg, nameAttr, name)
821 setattr(alg, selAttr, sel)
828 """Generic builder for the N-object pT selectors (EL_N, MU_N, JET_N,
829 PH_N, TAU_N, LJET_N): identical except for the source container, which
830 is always required since the cut acts on it."""
832 spec = getattr(self, attr)
836 thisalg = f
'{self.selectionName}_{tag}_{self.step}'
837 alg = config.createAlgorithm(
'CP::NObjectPtSelectorAlg', thisalg)
838 alg.particles, alg.objectSelection = config.readNameAndSelection(spec)
839 if attr
in (
"electrons",
"muons"):
843 config, spec.split(
".")[0], alg.objectSelection,
847 alg.count = self.
check_int(args[
'count'])
852 args = self.
parseArgs(
"IMPORT", text.split())
857 self.
currentDecoration = f
'{self.currentDecoration},as_char&&pass_{region}_%SYS%'
859 imported_cuts = [cut
for cut
in config.getSelectionCutFlow(
'EventInfo',
'')
if cut.startswith(region)]
863 args = self.
parseArgs(
"JET_N_BTAG", text.split())
865 thisalg = f
'{self.selectionName}_NBJET_{self.step}'
866 alg = config.createAlgorithm(
'CP::NObjectPtSelectorAlg', thisalg)
867 particles, selection = config.readNameAndSelection(self.jets)
868 alg.particles = particles
869 alg.objectSelection = f
'{selection}&&{self.btagDecoration},as_char' if selection
else f
'{self.btagDecoration},as_char'
872 customBtag = f
'ftag_select_{btagger}_{btagWP}'
873 alg.objectSelection = f
'{selection}&&{customBtag},as_char' if selection
else f
'{customBtag},as_char'
878 alg.count = self.
check_int(args[
'count'])
882 args = self.
parseArgs(
"SUM_EL_N_MU_N", text.split())
884 thisalg = f
'{self.selectionName}_SUMNELNMU_{self.step}'
885 alg = config.createAlgorithm(
'CP::SumNLeptonPtSelectorAlg', thisalg)
886 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
887 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
889 if args.get(
'ptmin')
is not None:
894 if args.get(
'selEl'):
897 if args.get(
'selMu'):
903 alg.count = self.
check_int(args[
'count'])
907 args = self.
parseArgs(
"SUM_EL_N_MU_N_TAU_N", text.split())
908 self.
_require_inputs(
"electrons",
"muons",
"taus", message=
"electrons, muons or taus")
909 thisalg = f
'{self.selectionName}_SUMNLEPTONS_{self.step}'
910 alg = config.createAlgorithm(
'CP::SumNLeptonPtSelectorAlg', thisalg)
911 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
912 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
913 alg.taus, alg.tauSelection = config.readNameAndSelection(self.
taus)
915 if args.get(
'ptmin')
is not None:
921 if args.get(
'selEl'):
924 if args.get(
'selMu'):
927 if args.get(
'selTau'):
934 alg.count = self.
check_int(args[
'count'])
938 args = self.
parseArgs(
"LJETMASS_N", text.split())
939 thisalg = f
'{self.selectionName}_NLJETMASS_{self.step}'
940 alg = config.createAlgorithm(
'CP::NObjectMassSelectorAlg', thisalg)
941 alg.particles, alg.objectSelection = config.readNameAndSelection(self.largeRjets)
944 config, self.largeRjets.
split(
".")[0], alg.objectSelection,
948 alg.count = self.
check_int(args[
'count'])
952 args = self.
parseArgs(
"LJETMASSWINDOW_N", text.split())
953 thisalg = f
'{self.selectionName}_NLJETMASSWINDOW_{self.step}'
954 alg = config.createAlgorithm(
'CP::NLargeRJetMassWindowSelectorAlg', thisalg)
955 alg.ljets, alg.ljetSelection = config.readNameAndSelection(self.largeRjets)
962 alg.count = self.
check_int(args[
'count'])
963 alg.vetoMode = args[
'veto']
967 args = self.
parseArgs(
"JET_N_GHOST", text.split())
968 thisalg = f
'{self.selectionName}_NJETGHOST_{self.step}'
969 alg = config.createAlgorithm(
'CP::JetNGhostSelectorAlg', thisalg)
970 alg.jets, alg.jetSelection = config.readNameAndSelection(self.jets)
972 alg.ghost = ghosts[0]
978 alg.count = self.
check_int(args[
'count'])
982 args = self.
parseArgs(
"LJET_N_GHOST", text.split())
983 thisalg = f
'{self.selectionName}_NLJETGHOST_{self.step}'
984 alg = config.createAlgorithm(
'CP::JetNGhostSelectorAlg', thisalg)
985 alg.jets, alg.jetSelection = config.readNameAndSelection(self.largeRjets)
987 alg.ghost = ghosts[0]
993 alg.count = self.
check_int(args[
'count'])
997 args = self.
parseArgs(
"OBJ_N", text.split())
998 thisalg = f
'{self.selectionName}_NOBJ_{self.step}'
999 alg = config.createAlgorithm(
'CP::NObjectPtSelectorAlg', thisalg)
1000 alg.particles, alg.objectSelection = config.readNameAndSelection(self.
check_string(args[
'container']))
1003 alg.count = self.
check_int(args[
'count'])
1007 args = self.
parseArgs(
"MET", text.split())
1009 thisalg = f
'{self.selectionName}_MET_{self.step}'
1010 alg = config.createAlgorithm(
'CP::MissingETSelectorAlg', thisalg)
1011 alg.met = config.readName(self.
met)
1012 alg.metTerm = self.metTerm
1018 args = self.
parseArgs(
"MWT", text.split())
1020 thisalg = f
'{self.selectionName}_MWT_{self.step}'
1021 alg = config.createAlgorithm(
'CP::TransverseMassSelectorAlg', thisalg)
1022 alg.met = config.readName(self.
met)
1023 alg.metTerm = self.metTerm
1024 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
1025 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
1032 args = self.
parseArgs(
"MET+MWT", text.split())
1035 thisalg = f
'{self.selectionName}_METMWT_{self.step}'
1036 alg = config.createAlgorithm(
'CP::MissingETPlusTransverseMassSelectorAlg', thisalg)
1037 alg.met = config.readName(self.
met)
1038 alg.metTerm = self.metTerm
1039 alg.electrons, alg.electronSelection = config.readNameAndSelection(self.
electrons)
1040 alg.muons, alg.muonSelection = config.readNameAndSelection(self.
muons)
1043 alg.refMETMWT = self.
check_float(args[
'refMETMWT'])
1047 args = self.
parseArgs(
"MLL", text.split())
1049 thisalg = f
'{self.selectionName}_MLL_{self.step}'
1050 alg = config.createAlgorithm(
'CP::DileptonInvariantMassSelectorAlg', thisalg)
1052 (self.
electrons, (
'electrons',
'electronSelection'),
1053 (
'truthElectrons',
'truthElectronSelection')),
1054 (self.
muons, (
'muons',
'muonSelection'),
1055 (
'truthMuons',
'truthMuonSelection')),
1057 if args[
'include_taus']:
1060 (self.
taus, (
'taus',
'tauSelection'),
1061 (
'truthTaus',
'truthTauSelection')),
1068 args = self.
parseArgs(
"MLLWINDOW", text.split())
1070 thisalg = f
'{self.selectionName}_MLLWINDOW_{self.step}'
1071 alg = config.createAlgorithm(
'CP::DileptonInvariantMassWindowSelectorAlg', thisalg)
1073 (self.
electrons, (
'electrons',
'electronSelection'),
1074 (
'truthElectrons',
'truthElectronSelection')),
1075 (self.
muons, (
'muons',
'muonSelection'),
1076 (
'truthMuons',
'truthMuonSelection')),
1078 if args[
'include_taus']:
1081 (self.
taus, (
'taus',
'tauSelection'),
1082 (
'truthTaus',
'truthTauSelection')),
1086 alg.vetoMode = args[
'veto']
1090 """Builder shared by OS and SS: same algorithm, opposite charge mode."""
1091 args = self.
parseArgs(tag, text.split())
1093 thisalg = f
'{self.selectionName}_{tag}_{self.step}'
1094 alg = config.createAlgorithm(
'CP::ChargeSelectorAlg', thisalg)
1095 allLeptons =
not (args[
'el']
or args[
'mu']
or args[
'tau'])
1096 if self.
electrons and (allLeptons
or args[
'el']):
1098 (
'electrons',
'electronSelection'),
1099 (
'truthElectrons',
'truthElectronSelection'))
1100 if self.
muons and (allLeptons
or args[
'mu']):
1102 (
'muons',
'muonSelection'),
1103 (
'truthMuons',
'truthMuonSelection'))
1104 if self.
taus and (allLeptons
or args[
'tau']):
1106 (
'taus',
'tauSelection'),
1107 (
'truthTaus',
'truthTauSelection'))
1112 args = self.
parseArgs(
"MLL_OSSF", text.split())
1114 thisalg = f
'{self.selectionName}_MLL_OSSF_{self.step}'
1115 alg = config.createAlgorithm(
'CP::DileptonOSSFInvariantMassWindowSelectorAlg', thisalg)
1117 (self.
electrons, (
'electrons',
'electronSelection'),
1118 (
'truthElectrons',
'truthElectronSelection')),
1119 (self.
muons, (
'muons',
'muonSelection'),
1120 (
'truthMuons',
'truthMuonSelection')),
1122 if args[
'include_taus']:
1125 (self.
taus, (
'taus',
'tauSelection'),
1126 (
'truthTaus',
'truthTauSelection')),
1130 alg.vetoMode = args[
'veto']
1134 args = self.
parseArgs(
"EVENTFLAG", text.split())
1135 existingDecoration = self.
check_string(args[
'decoration'])
1139 args = self.
parseArgs(
"GLOBALTRIGMATCH", text.split())
1141 self.
setDecorationName(
None, config, f
"globalTriggerMatch{postfix}_%SYS%,as_char")
1144 args = self.
parseArgs(
"RUN_NUMBER", text.split())
1145 thisalg = f
'{self.selectionName}_RUN_NUMBER_{self.step}'
1146 alg = config.createAlgorithm(
'CP::RunNumberSelectorAlg', thisalg)
1148 alg.runNumber = self.
check_int(args[
'runNumber'])
1149 alg.useRandomRunNumber = config.dataType()
is not DataType.Data
1180 _EXPR_TOKEN_RE = re.compile(
r"""
1181 (?P<NUM>\d+\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+|\d+)
1182 | (?P<GE>>=) | (?P<LE><=) | (?P<EQ>==) | (?P<LT><) | (?P<GT>>)
1183 | (?P<ID>[A-Za-z_][A-Za-z0-9_]*)
1184 | (?P<LP>\() | (?P<RP>\)) | (?P<LB>\[) | (?P<RB>\])
1185 | (?P<COMMA>,) | (?P<PLUS>\+) | (?P<MINUS>-)
1199 f
"[EventSelectionConfig] EXPR: variable '{variable}' is not available. "
1200 "Please request it from the EventSelectionAlgorithms developers.")
1202 minN = var_spec.min_operands
1203 maxN = var_spec.max_operands
1204 sep = var_spec.separator
1205 metOk = var_spec.met_ok
1207 if n < minN
or (maxN
is not None and n > maxN):
1208 expected = f
"{minN}" if maxN == minN
else (f
"{minN}+" if maxN
is None else f
"{minN}-{maxN}")
1210 f
"[EventSelectionConfig] EXPR: '{variable}' takes {expected} operand(s), got {n}")
1211 if n > 1
and separator != sep:
1212 want = {
",":
"','",
"+":
"'+'"}.
get(sep, str(sep))
1214 f
"[EventSelectionConfig] EXPR: '{variable}' operands must be separated by {want}")
1215 for coll, index
in operands:
1218 f
"[EventSelectionConfig] EXPR: collection '{coll}' is not available. "
1219 "Please request it from the EventSelectionAlgorithms developers.")
1221 isMET = coll_spec.is_met
1225 f
"[EventSelectionConfig] EXPR: 'met' is not valid for '{variable}'")
1226 if index
is not None:
1228 "[EventSelectionConfig] EXPR: 'met' cannot be indexed")
1229 if separator ==
"+":
1231 "[EventSelectionConfig] EXPR: 'met' cannot be combined in a sum")
1234 f
"[EventSelectionConfig] EXPR: '{coll}' must be indexed, e.g. {coll}[0]")
1237 body = text[len(
"EXPR"):].
strip()
1243 thisalg = f
'{self.selectionName}_EXPR_{self.step}'
1244 alg = config.createAlgorithm(
'CP::ObjectKinematicSelectorAlg', thisalg)
1245 alg.variable = variable
1247 alg.refValue = refValue
1249 operandKinds, collections, selections, indices = [], [], [], []
1251 for coll, index
in operands:
1253 opt = coll_spec.option
1254 applyBtag = coll_spec.apply_btag
1255 isMET = coll_spec.is_met
1256 container = getattr(self, opt)
1260 operandKinds.append(
"MET")
1263 operandKinds.append(
"PARTICLE")
1264 name, selection = config.readNameAndSelection(container)
1268 selection = (f
'{selection}&&{self.btagDecoration},as_char'
1269 if selection
else f
'{self.btagDecoration},as_char')
1270 collections.append(name)
1271 selections.append(selection)
1272 indices.append(index)
1274 alg.operandKinds = operandKinds
1275 alg.collections = collections
1276 alg.selections = selections
1277 alg.indices = indices
1281 alg.met = config.readName(self.
met)
1282 alg.metTerm = self.metTerm
1288 _EVENTVAR_TYPES = {
"float":
"floatVariable",
"int":
"intVariable",
"double":
"doubleVariable"}
1292 args = self.
parseArgs(
"EVENTVAR", text.split())
1295 self.
raise_misconfig(text, f
"value type (one of {sorted(self._EVENTVAR_TYPES)})")
1297 thisalg = f
'{self.selectionName}_EVENTVAR_{self.step}'
1298 alg = config.createAlgorithm(
'CP::EventScalarSelectorAlg', thisalg)
1302 alg.refValue = self.
check_float(args[
'value'], requirePositive=
False)
1311 "[EventSelectionConfig] The 'SAVE' keyword is deprecated: the event "
1312 "filter is now created automatically at the end of each EventSelection "
1313 f
"block. Please remove the 'SAVE' line from selection '{self.selectionName}'.",
1314 category=ConfigDeprecationWarning, stacklevel=2)
1317 """Create the SaveFilterAlg that turns the accumulated event selection
1318 into a named, persisted selection (and ntuple branch). Called once per
1319 block, automatically at the end of makeAlgs."""
1320 thisalg = f
'{self.selectionName}_SAVE'
1321 alg = config.createAlgorithm(
'CP::SaveFilterAlg', thisalg)
1322 alg.FilterDescription = f
'events passing < {self.selectionName} >'
1323 alg.eventDecisionOutputDecoration = f
'ignore_{self.selectionName}_%SYS%'
1326 alg.selectionName = f
'pass_{self.selectionName}_%SYS%,as_char'
1327 alg.decorationName = f
'ntuplepass_{self.selectionName}_%SYS%'
1328 config.addOutputVar(
'EventInfo', f
'ntuplepass_{self.selectionName}_%SYS%', f
'pass_{self.selectionName}')
1334 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