5 """Helper class to define and merge step outputs"""
7 from __future__
import annotations
9 from collections.abc
import Mapping
10 from typing
import Any, Iterable, Iterator, Optional
13 from AthenaConfiguration.ComponentAccumulator
import ComponentAccumulator
14 from AthenaConfiguration.AccumulatorCache
import AccumulatorCachable
16 log = logging.getLogger(__name__)
19 class StepOutput(AccumulatorCachable, Mapping[str, Any]):
20 """Helper class to define and merge step outputs
22 The reconstruction sequences for the trigger are broken down into steps. This class
24 - A list of ComponentAccumulators (one per active step)
25 - A dictionary containing nicknames for the outputs of the current steps
27 Dictionary access on this object accesses output names.
29 Note that the step indices here are the steps specific to the jet/MET sequences,
30 not the full number of steps. At present there are three of these:
31 0: Calo-only reconstruction
32 1: Jet-RoI tracking (MET does not participate)
35 However, this particular class does not make any requirements on what these steps
36 are or how many there are.
40 self, steps: Iterable[ComponentAccumulator]=(), outputs: dict[str, Any]={}
48 ca: ComponentAccumulator,
49 inputs: Optional[StepOutput] =
None,
50 step_idx: Optional[int] =
None,
53 """Helper method to create a step output from a single CA and its inputs
57 ca : ComponentAccumulator
58 The main component accumulator
59 inputs : Optional[StepOutput]
60 The inputs (if any) to the the main CA
61 step_idx : Optional[int]
62 Allow specifying a step. If not provided will be the max step out of the
63 inputs or 0 if there are none
65 Any named outputs from this step
68 step_idx = inputs.max_step_idx
if inputs
else 0
70 steps[step_idx].
merge(ca)
73 output.merge_other(inputs)
77 def merge(cls, *inputs: StepOutput) -> StepOutput:
78 """Form a new output by merging multiple others"""
86 """Get the maximum step index in this output"""
87 return len(self.
_steps) - 1
90 def steps(self) -> tuple[ComponentAccumulator, ...]:
91 """The step-separated ComponentAccumulators"""
95 """Add a prefix to all of our outputs to prevent conflicts"""
96 self.
_outputs = {prefix + k: v
for k, v
in self.items()}
99 self, other: StepOutput, rename_outputs: dict[str, str] = {}
101 """Merge another StepOutput into this
106 The other output to merge
107 rename_outputs : dict[str, str]
108 Optionally rename some output names so there are no clashes
110 for step_idx, ca
in enumerate(other.steps):
113 {rename_outputs.get(k, k): v
for k, v
in other._outputs.items()}
116 def merge_ca(self, ca: ComponentAccumulator, step_idx: int, /, **outputs: Any) ->
None:
117 """Merge a single CA into this
121 ca : ComponentAccumulator
122 The component accumulator to merge
124 The step index in which to merge it
126 Any named outputs from the CA
137 if self[key] != outputs[key]
140 log.error(
"Incompatible definitions for named outputs: ")
142 log.error(f
"\t{key}: {self[key]} -> {outputs[key]}")
143 raise ValueError(
str(overlap))
156 for ca
in self.
steps: