ATLAS Offline Software
ConfigHelpers.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 """ Helper functions for configuring MET chains"""
4 
5 from __future__ import annotations
6 from typing import Any, Optional
7 
8 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
9 from AthenaConfiguration.ComponentFactory import CompFactory
10 from ..Menu.SignatureDicts import METChainParts_Default, METChainParts
11 from ..Config.MenuComponents import (
12  ChainStep,
13  InEventRecoCA,
14  SelectionCA,
15  MenuSequenceCA,
16 )
17 from .StepOutput import StepOutput
18 from copy import copy
19 from ..CommonSequences.FullScanDefs import trkFSRoI
20 from AthenaCommon.Logging import logging
21 from TrigEFMissingET.TrigEFMissingETConfig import getMETMonTool
22 from abc import ABC, abstractmethod
23 from string import ascii_uppercase
24 from TrigMissingETHypo.TrigMissingETHypoConfig import TrigMETHypoToolFromDict
25 from DecisionHandling.DecisionHandlingConfig import ComboHypoCfg
26 import functools
27 
28 def streamer_hypo_tool(chainDict):
29  return CompFactory.TrigStreamerHypoTool(chainDict["chainName"])
30 
31 
32 log = logging.getLogger(__name__)
33 
34 # The keys from the MET chain dict that directly affect reconstruction
35 # The order here is important as it also controls the dict -> string conversion
36 
37 recoKeys = [
38  "EFrecoAlg",
39  "calib",
40  "constitType",
41  "constitmod",
42  "jetCalib",
43  "nSigma",
44  "addInfo",
45 ]
46 metFSRoIs: list[str | None] = ["", None, trkFSRoI]
47 
48 
49 def metRecoDictToString(recoDict: dict[str, str], skipDefaults: bool = True) -> str:
50  """Convert a dictionary containing reconstruction keys to a string
51 
52  Any key (from recoKeys) missing will just be skipped.
53 
54  Parameters
55  ----------
56  recoDict : dict[str, str]
57  The reconstruction dictionary
58  skipDefaults : bool, optional
59  If true, skip any values that match the default, by default True
60 
61  Returns
62  -------
63  str
64  The fixed string representation
65  """
66  return "_".join(
67  recoDict[k]
68  for k in recoKeys
69  if k in recoDict
70  and (not skipDefaults or recoDict[k] != METChainParts_Default[k])
71  )
72 
73 
74 def stringToMETRecoDict(string: str, onlyRecoKeys: bool = False) -> dict[str, Any]:
75  """Convert a string to a MET reco dict. Optionally only keep entries contained in recoKeys.
76  Necessary for correctly configuring algorithms as inputs to NN.
77  """
78  defaults = copy(METChainParts_Default)
79  if onlyRecoKeys:
80  defaults = {key: defaults[key] for key in recoKeys}
81  # Now go through the parts of the string and fill in the dict
82  for part in string.split("_"):
83  for key, values in METChainParts.items():
84  if not isinstance(values, list):
85  continue
86  if part in values:
87  if isinstance(defaults[key], list):
88  defaults[key].append(part) # type: ignore[attr-defined]
89  else:
90  defaults[key] = part
91  return defaults
92 
93 
94 class AlgConfig(ABC):
95  """Base class to describe algorithm configurations
96 
97  Each individual 'EFrecoAlg' should be described by *one* AlgConfig subclass.
98  It must provide its list of required inputs to the constructor and override
99  the make_fex method
100 
101  The name of fexAlg *must* be self.fexName and the METContainerKey property
102  *must* be set to self.outputKey (but this class usually should take care of
103  this).
104 
105  The subclass must also implement the @classmethod 'algType' which returns
106  the EFrecoAlg string that it describes.
107  """
108 
109  @classmethod
110  def algType(cls) -> str:
111  """The algorithm that this object configures - this corresponds to the
112  EFrecoAlg in the METChainParts dictionary
113 
114  Note that no two subclasses of AlgConfig can describe the same algorithm
115  (as identified by this string).
116  """
117  raise NotImplementedError("algType not implemented by subclass!")
118 
119  def __init__(self, **recoDict: str):
120  """Initialise the base class
121 
122  =========
123  Arguments
124  =========
125  recoDict: Pass *all* the keys required for the recoDict
126  """
127 
128  # Make sure that we got *all* the keys (i.e. the subclass didn't
129  # inadvertently steal one of them from us)
130  alg_type = self.algType()
131  assert all(k in recoDict for k in recoKeys), (
132  f"AlgConfig.__init__ for {alg_type} did not receive all the recoKeys"
133  " - this suggests a problem in the subclass __init__ method!"
134  )
135  self.recoDict = copy(recoDict)
136  self._suffix = metRecoDictToString(recoDict)
137 
138  @property
139  def outputKey(self) -> str:
140  """The MET container object produced by this algorithm"""
141  from TrigEDMConfig.TriggerEDM import recordable
142 
143  return recordable("HLT_MET_{}".format(self._suffix))
144 
145  @property
146  def fexName(self) -> str:
147  """The name of the algorithm made by this configuration"""
148  return "EFMET_{}".format(self._suffix)
149 
150  def interpret_reco_dict(self) -> dict[str, Any]:
151  """Return a version of the reco dict with any necessary post-processing"""
152  return self.recoDict
153 
154  def getMonTool(self, flags):
155  """Create the monitoring tool"""
156  return getMETMonTool(flags)
157 
158  def name_step(self, idx) -> str:
159  return f"step{ascii_uppercase[idx]}_{self._suffix}"
160 
161  @abstractmethod
162  def make_reco_algs(self, flags, **recoDict) -> StepOutput:
163  """Create the reconstruction sequences including the FEX
164 
165  Returns a list of CAs split by step
166  """
167 
168  def _append_fex(self, flags, fex, inputs: Optional[StepOutput] = None) -> None:
169  """Append the FEX to the output object
170 
171  Finalizes the FEX by setting the monitoring tool and output name.
172  """
173  fex.MonTool = self.getMonTool(flags)
174  fex.METContainerKey = self.outputKey
175  acc = ComponentAccumulator()
176  acc.addEventAlgo(fex, primary=True)
177  return StepOutput.create(acc, inputs, MET=self.outputKey)
178 
179  def make_reco_ca(self, flags):
180  """Make the reconstruction sequences for the new JO style"""
181  # Retrieve the inputs
182  log.verbose("Create inputs for %s", self._suffix)
183  steps, inputs = self.inputRegistry.build_steps(
184  flags, self._inputs, metFSRoIs, self.recoDict, return_ca=True
185  )
186  # Create the FEX and add it to the last input sequence
187  fex = self.make_fex_accumulator(flags, self.fexName, inputs)
188  fex.MonTool = self.getMonTool(flags)
189  fex.METContainerKey = self.outputKey
190  steps[-1].addEventAlgo(fex)
191  return steps
192 
193  def make_accumulator_steps(self, flags, chainDict):
194  """Make the full accumulator steps"""
195 
196  # Get the reco sequences
197  reco_sequences = self.make_reco_algs(flags, **self.interpret_reco_dict()).steps
198  output_steps = []
199  # build up the output steps
200  # We have to merge together the CAs containing the reconstruction sequences
201  # and the InEventRecoCAs that contain the input makers
202  for step_idx, reco_sequence in enumerate(reco_sequences):
203  reco_acc = self.inputMakerCA(step_idx)
204  sel_acc = None
205  step_name = f"Empty_METStep{step_idx}"
206  if (
207  reco_acc is not None
208  ): # Define reco and hypo algorithms only if reco_acc is not None
209  if step_idx == len(reco_sequences) - 1:
210  # If this is the last step we have to add the hypo alg
211  hypo_alg = self.make_hypo_alg()
212  hypo_tool = TrigMETHypoToolFromDict
213  else:
214  # Otherwise, we add a passthrough hypo
215  hypo_alg = self.make_passthrough_hypo_alg(step_idx)
216  hypo_tool = streamer_hypo_tool
217 
218  # Now merge the reco sequence into the InEventRecoCA
219  reco_acc.mergeReco(reco_sequence)
220 
221  # Create a selection CA
222  sel_acc = SelectionCA("METAthSeq_" + self.name_step(step_idx))
223  # Merge in the reconstruction sequence
224  sel_acc.mergeReco(reco_acc)
225  # Add its hypo alg
226  sel_acc.addHypoAlgo(hypo_alg)
227 
228  # FIXME: We need to mark the CA as merged because during the fast menu generation
229  # the merging is only done once for all chains, which is not compatible with the MET
230  # menu generation code. Remove this once ATR-29211 is fixed.
231  sel_acc.wasMerged()
232 
233  step_name = sel_acc.name
234 
235  # Build the menu sequence and create the actual chainStep
236  output_steps.append(
237  ChainStep(
238  name=step_name,
239  multiplicity=[] if sel_acc is None else [1],
240  chainDicts=[chainDict],
241  Sequences=[]
242  if sel_acc is None
243  else [
244  functools.partial(make_MET_menu_sequenceGenCfg, flags, sel_acc, hypo_tool)
245  ],
246  comboHypoCfg=ComboHypoCfg
247  )
248  )
249 
250  return output_steps
251 
252 
253  def make_hypo_alg(self):
254  """The hypo alg used for this configuration"""
255  return CompFactory.TrigMissingETHypoAlg(
256  f"METHypoAlg_{self._suffix}", METContainerKey=self.outputKey
257  )
258 
259  def make_passthrough_hypo_alg(self, step):
260  return CompFactory.TrigStreamerHypoAlg(
261  "METPassThroughHypo_" + self.name_step(step)
262  )
263 
264  def inputMakerCA(self, idx):
265  """Get the InEventRecoCA for the given step index"""
266  from TrigT2CaloCommon.CaloDef import clusterFSInputMaker
267  from AthenaConfiguration.ComponentFactory import CompFactory
268  from ..CommonSequences.FullScanDefs import trkFSRoI
269 
270  name = self.name_step(idx) + "Reco"
271  if idx == 0:
272  return InEventRecoCA(name, inputMaker=clusterFSInputMaker())
273  elif idx == 1:
274  return None
275  elif idx == 2:
276  return InEventRecoCA(
277  name,
278  inputMaker=CompFactory.InputMakerForRoI(
279  "IM_Jet_TrackingStep",
280  RoITool=CompFactory.ViewCreatorInitialROITool(),
281  RoIs=trkFSRoI,
282  ),
283  )
284  else:
285  raise KeyError(f"No input maker for step {idx}")
286 
287  @classmethod
288  def _get_subclasses(cls):
289  """Provides a way to iterate over all subclasses of this class"""
290  for subcls in cls.__subclasses__():
291  for subsubcls in subcls.__subclasses__():
292  yield subsubcls
293  yield subcls
294 
295  @classmethod
296  def fromRecoDict(cls, EFrecoAlg, **recoDict) -> AlgConfig:
297  for subcls in cls._get_subclasses():
298  if subcls.algType() == EFrecoAlg:
299  return subcls(EFrecoAlg=EFrecoAlg, **recoDict)
300 
301  raise ValueError("Unknown EFrecoAlg '{}' requested".format(EFrecoAlg))
302 
303 def make_MET_menu_sequenceGenCfg(flags, sel_acc, hypo_tool):
304  return MenuSequenceCA(flags, selectionCA=sel_acc, HypoToolGen=hypo_tool)
305 
306 # Load all the defined configurations
307 from . import AlgConfigs
308 
309 # Make sure that there is an AlgConfig for every EFrecoAlg
310 AlgConfigs.test_configs()
python.HLT.MET.ConfigHelpers.AlgConfig._append_fex
None _append_fex(self, flags, fex, Optional[StepOutput] inputs=None)
Definition: ConfigHelpers.py:168
python.JetAnalysisCommon.ComponentAccumulator
ComponentAccumulator
Definition: JetAnalysisCommon.py:302
vtune_athena.format
format
Definition: vtune_athena.py:14
python.HLT.MET.ConfigHelpers.AlgConfig.make_hypo_alg
def make_hypo_alg(self)
Definition: ConfigHelpers.py:253
python.HLT.MET.ConfigHelpers.AlgConfig.make_reco_ca
def make_reco_ca(self, flags)
Definition: ConfigHelpers.py:179
python.HLT.MET.ConfigHelpers.AlgConfig.make_reco_algs
StepOutput make_reco_algs(self, flags, **recoDict)
Definition: ConfigHelpers.py:162
python.HLT.MET.ConfigHelpers.metRecoDictToString
str metRecoDictToString(dict[str, str] recoDict, bool skipDefaults=True)
Definition: ConfigHelpers.py:49
python.HLT.MET.ConfigHelpers.AlgConfig._get_subclasses
def _get_subclasses(cls)
Definition: ConfigHelpers.py:288
python.HLT.MET.ConfigHelpers.AlgConfig.name_step
str name_step(self, idx)
Definition: ConfigHelpers.py:158
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
CaloDef.clusterFSInputMaker
def clusterFSInputMaker()
cluster maker functions
Definition: CaloDef.py:51
python.HLT.MET.ConfigHelpers.AlgConfig._suffix
_suffix
Definition: ConfigHelpers.py:136
python.HLT.MET.ConfigHelpers.AlgConfig.inputMakerCA
def inputMakerCA(self, idx)
Definition: ConfigHelpers.py:264
python.HLT.MET.ConfigHelpers.AlgConfig.interpret_reco_dict
dict[str, Any] interpret_reco_dict(self)
Definition: ConfigHelpers.py:150
python.HLT.MET.ConfigHelpers.streamer_hypo_tool
def streamer_hypo_tool(chainDict)
Definition: ConfigHelpers.py:28
python.HLT.MET.ConfigHelpers.AlgConfig.algType
str algType(cls)
Definition: ConfigHelpers.py:110
TrigEFMissingETConfig.getMETMonTool
def getMETMonTool(flags, name="MonTool")
Definition: TrigEFMissingETConfig.py:5
python.HLT.MET.ConfigHelpers.AlgConfig.__init__
def __init__(self, **str recoDict)
Definition: ConfigHelpers.py:119
python.HLT.MET.ConfigHelpers.AlgConfig
Definition: ConfigHelpers.py:94
python.HLT.MET.ConfigHelpers.AlgConfig.recoDict
recoDict
Definition: ConfigHelpers.py:135
python.HLT.MET.ConfigHelpers.AlgConfig.getMonTool
def getMonTool(self, flags)
Definition: ConfigHelpers.py:154
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.HLT.MET.ConfigHelpers.AlgConfig.fromRecoDict
AlgConfig fromRecoDict(cls, EFrecoAlg, **recoDict)
Definition: ConfigHelpers.py:296
python.HLT.MET.ConfigHelpers.AlgConfig.outputKey
str outputKey(self)
Definition: ConfigHelpers.py:139
Cut::all
@ all
Definition: SUSYToolsAlg.cxx:64
python.HLT.MET.ConfigHelpers.AlgConfig.fexName
str fexName(self)
Definition: ConfigHelpers.py:146
python.HLT.MET.ConfigHelpers.make_MET_menu_sequenceGenCfg
def make_MET_menu_sequenceGenCfg(flags, sel_acc, hypo_tool)
Definition: ConfigHelpers.py:303
python.TriggerEDM.recordable
def recordable(arg, runVersion=3)
Definition: TriggerEDM.py:30
python.HLT.MET.ConfigHelpers.AlgConfig.make_passthrough_hypo_alg
def make_passthrough_hypo_alg(self, step)
Definition: ConfigHelpers.py:259
calibdata.copy
bool copy
Definition: calibdata.py:27
python.HLT.MET.ConfigHelpers.stringToMETRecoDict
dict[str, Any] stringToMETRecoDict(str string, bool onlyRecoKeys=False)
Definition: ConfigHelpers.py:74
python.HLT.MET.ConfigHelpers.AlgConfig.make_accumulator_steps
def make_accumulator_steps(self, flags, chainDict)
Definition: ConfigHelpers.py:193