ATLAS Offline Software
ConfigFactory.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 # This file defines a factory method that can create a configuration
4 # block sequence based on a passed in name. This avoids having to
5 # import all the various config block sequence makers in the
6 # configuration code, and also would make it easier to create them
7 # from a text configuration file.
8 
9 # This relies heavily on the blocks exposing all configurable
10 # parameters as options, since there is no other mechanism to
11 # configure them through this interface.
12 
13 # The implementation itself is probably not the best possible, it
14 # lacks all extensibility, gathers all information in a single place,
15 # etc. Still for now (08 Dec 22) this ought to be good enough.
16 
17 
18 import inspect
19 from AnalysisAlgorithmsConfig.ConfigSequence import ConfigSequence
20 
21 from AnaAlgorithm.Logging import logging
22 logCPAlgCfgFactory = logging.getLogger('CPAlgCfgFactory')
23 
24 
25 def getDefaultArgs(func):
26  """return dict(par, val) with all func parameters with defualt values"""
27  signature = inspect.signature(func)
28  return {
29  k: v.default
30  for k, v in signature.parameters.items()
31  if v.default is not inspect.Parameter.empty
32  }
33 
34 
35 def getFuncArgs(func):
36  """return list of input parameters"""
37  if inspect.isclass(func):
38  args = list(inspect.signature(func.__init__).parameters.keys())
39  args.remove('self')
40  else:
41  args = list(inspect.signature(func).parameters.keys())
42  return args
43 
44 
45 # class for config block information
46 class FactoryBlock():
47  """
48  """
49  def __init__(self, alg, algName, options, defaults, subAlgs=None):
50  self.alg = alg
51  self.algName = algName
52  self.options = options
53  self.defaults = defaults
54  if subAlgs is None:
55  self.subAlgs = {}
56  else:
57  self.subAlgs = subAlgs
58 
59 
60  def makeConfig(self, funcOptions):
61  """
62  Parameters
63  ----------
64  funcName: str
65  name associated with the algorithm. This name must have been added to the
66  list of available algorithms
67  funcOptions: dict
68  dictionary containing options for the algorithm read from the YAML file
69 
70  Returns
71  -------
72  configSequence
73  """
74  configSeq = ConfigSequence()
75 
76  func = self.alg
77  funcName = self.algName
78  funcDefaults = getDefaultArgs(func)
79  defaults = self.defaults
80 
81  args = {}
82  # loop over all options for the function
83  for arg in getFuncArgs(func):
84  # supplied from config file
85  if arg in funcOptions:
86  args[arg] = funcOptions[arg]
87  # defaults set in function def
88  elif arg in funcDefaults:
89  args[arg] = funcDefaults[arg]
90  # defaults provided when func was added
91  elif defaults is not None and arg in defaults:
92  args[arg] = defaults[arg]
93  elif arg == 'seq':
94  # 'seq' should be first arg of func (not needed for class)
95  args[arg] = configSeq
96  elif arg == 'kwargs':
97  # cannot handle arbitrary parameters
98  continue
99  else:
100  raise ValueError(f"{arg} is required for {funcName}")
101  if inspect.isclass(func):
102  configSeq.append(func(**args))
103  else:
104  func(**args)
105  return configSeq, args.keys()
106 
107 
109  """This class provides a configuration manager that is intended to allow the user to:
110  - define and configure functions that return an algSequence(?) object
111  """
112  def __init__(self, addDefaultBlocks=True):
113  self.ROOTNAME = 'root' # constant
114  self._algs = {}
115  self._order = {self.ROOTNAME: []}
116  if addDefaultBlocks:
117  self.addDefaultAlgs()
118 
119 
120  def addAlgConfigBlock(self, algName, alg, defaults=None, pos=None, superBlocks=None):
121  """Add class to list of available algorithms"""
122  if not callable(alg):
123  raise ValueError(f"{algName} is not a callable.")
124  opts = getFuncArgs(alg)
125 
126  if superBlocks is None:
127  superBlocks = [self.ROOTNAME]
128  elif not isinstance(superBlocks, list):
129  superBlocks = [superBlocks]
130 
131  # add new alg block to subAlgs dict of super block
132  for block in superBlocks:
133  if block not in self._order:
134  self._order[block] = []
135  order = self._order[block]
136 
137  if block == self.ROOTNAME:
138  algs = self._algs
139  else:
140  if block not in self._algs:
141  raise ValueError(f"{block} not added")
142  algs = self._algs[block].subAlgs
143 
144  if alg in algs:
145  raise ValueError(f"{algName} has already been added.")
146 
147  # create FactoryBlock with alg information
148  algs[algName] = FactoryBlock(
149  alg=alg,
150  algName=algName,
151  options=opts,
152  defaults=defaults,
153  subAlgs={}
154  )
155  # insert into order (list)
156  if pos is None:
157  order.append(algName)
158  elif pos in order:
159  order.insert(order.index(pos), algName)
160  else:
161  raise ValueError(f"{pos} does not exit in already added config blocks")
162  return
163 
164 
165  def printAlgs(self, printOpts=True):
166  """Prints algorithms exposed to configuration"""
167  printed = [] # some subblocks exist for multiple superblocks
168  def printAlg(algs):
169  for alg, algInfo in algs.items():
170  algName = algInfo.alg.__name__
171  algOptions = algInfo.options
172  if algName not in printed:
173  printed.append(algName)
174  logCPAlgCfgFactory.info(f"\033[4m{alg}\033[0m -> \033[4m{algName}\033[0m")
175  if printOpts:
176  try:
177  if inspect.isclass(algInfo.alg):
178  # block
179  algInfo.alg().printOptions(verbose=printOpts)
180  else:
181  # make function
182  seq = ConfigSequence()
183  algInfo.alg(seq=seq)
184  seq.printOptions(verbose=printOpts)
185  except Exception:
186  # either a TypeError or something else due to missing args
187  # try to print something for casses with required args
188  for opt in algOptions:
189  logCPAlgCfgFactory.info(f" {opt}")
190  printAlg(algInfo.subAlgs)
191  printAlg(self._algs)
192  return
193 
194 
195  def makeConfig(self, name, **kwargs):
196  """
197  Returns:
198  configSeq: configSequence object
199  """
200  try:
201  if '.' in name:
202  algContext, algName = name.split('.')
203  block = self._algs[algContext].subAlgs[algName]
204  else:
205  block = self._algs[name]
206  except KeyError:
207  raise ValueError(f"{name} config block not found. Make sure context is correct.")
208  configSeq, _ = block.makeConfig(kwargs)
209  return configSeq
210 
211 
212  def addDefaultAlgs(self):
213  """add algorithms and options"""
214 
215  # CommonServices
216  from AsgAnalysisAlgorithms.AsgAnalysisConfig import CommonServicesConfig
217  self.addAlgConfigBlock(algName="CommonServices", alg=CommonServicesConfig)
218 
219  # pileup reweighting
220  from AsgAnalysisAlgorithms.AsgAnalysisConfig import PileupReweightingBlock
221  self.addAlgConfigBlock(algName="PileupReweighting", alg=PileupReweightingBlock)
222 
223  # event cleaning
224  from AsgAnalysisAlgorithms.EventCleaningConfig import EventCleaningBlock
225  self.addAlgConfigBlock(algName="EventCleaning", alg=EventCleaningBlock)
226 
227  # trigger
228  from TriggerAnalysisAlgorithms.TriggerAnalysisConfig import Trigger
229  self.addAlgConfigBlock(algName="Trigger", alg=Trigger)
230 
231  # jets
232  from JetAnalysisAlgorithms.JetAnalysisConfig import makeJetAnalysisConfig
233  self.addAlgConfigBlock(algName="Jets", alg=makeJetAnalysisConfig)
234  from JetAnalysisAlgorithms.JetJvtAnalysisConfig import JetJvtAnalysisConfig
235  self.addAlgConfigBlock(algName="JVT", alg=JetJvtAnalysisConfig,
236  superBlocks="Jets")
237  from FTagAnalysisAlgorithms.FTagAnalysisConfig import FTagConfig
238  self.addAlgConfigBlock(algName="FlavourTagging", alg=FTagConfig,
239  defaults={'selectionName': ''},
240  superBlocks="Jets")
241  from FTagAnalysisAlgorithms.FTagEventSFAnalysisConfig import FTagEventSFConfig
242  self.addAlgConfigBlock(algName="FlavourTaggingEventSF",
243  alg=FTagEventSFConfig,
244  defaults={'selectionName': ''},
245  superBlocks="Jets")
246 
247  # electrons
248  from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronCalibrationConfig
249  self.addAlgConfigBlock(algName="Electrons", alg=ElectronCalibrationConfig)
250  from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronWorkingPointConfig
251  self.addAlgConfigBlock(algName="WorkingPoint", alg=ElectronWorkingPointConfig,
252  superBlocks="Electrons")
253  from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronTriggerAnalysisSFBlock
254  self.addAlgConfigBlock(algName="TriggerSF", alg=ElectronTriggerAnalysisSFBlock,
255  superBlocks="Electrons")
256 
257  # photons
258  from EgammaAnalysisAlgorithms.PhotonAnalysisConfig import PhotonCalibrationConfig
259  self.addAlgConfigBlock(algName="Photons", alg=PhotonCalibrationConfig)
260  from EgammaAnalysisAlgorithms.PhotonAnalysisConfig import PhotonWorkingPointConfig
261  self.addAlgConfigBlock(algName="WorkingPoint", alg=PhotonWorkingPointConfig,
262  superBlocks="Photons")
263  from EgammaAnalysisAlgorithms.PhotonExtraVariablesConfig import PhotonExtraVariablesBlock
264  self.addAlgConfigBlock(algName="ExtraVariables", alg=PhotonExtraVariablesBlock,
265  superBlocks="Photons")
266 
267  # muons
268  from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonCalibrationConfig
269  self.addAlgConfigBlock(algName="Muons", alg=MuonCalibrationConfig)
270  from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonWorkingPointConfig
271  self.addAlgConfigBlock(algName="WorkingPoint", alg=MuonWorkingPointConfig,
272  superBlocks="Muons")
273  from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonTriggerAnalysisSFBlock
274  self.addAlgConfigBlock(algName="TriggerSF", alg=MuonTriggerAnalysisSFBlock,
275  superBlocks="Muons")
276 
277  # tauJets
278  from TauAnalysisAlgorithms.TauAnalysisConfig import TauCalibrationConfig
279  self.addAlgConfigBlock(algName="TauJets", alg=TauCalibrationConfig)
280  from TauAnalysisAlgorithms.TauAnalysisConfig import TauWorkingPointConfig
281  self.addAlgConfigBlock(algName="WorkingPoint", alg=TauWorkingPointConfig,
282  superBlocks="TauJets")
283  from TauAnalysisAlgorithms.TauAnalysisConfig import TauTriggerAnalysisSFBlock
284  self.addAlgConfigBlock(algName="TriggerSF", alg=TauTriggerAnalysisSFBlock,
285  superBlocks="TauJets")
286 
287  # SystObjectLink
288  from AsgAnalysisAlgorithms.SystObjectLinkConfig import SystObjectLinkBlock
289  self.addAlgConfigBlock(algName="SystObjectLink", alg=SystObjectLinkBlock,
290  superBlocks=[self.ROOTNAME, "Jets", "Electrons", "Photons", "Muons", "TauJets"])
291 
292  # IFF truth classification
293  from AsgAnalysisAlgorithms.AsgAnalysisConfig import IFFLeptonDecorationBlock
294  self.addAlgConfigBlock(algName="IFFClassification", alg=IFFLeptonDecorationBlock,
295  superBlocks=["Electrons","Muons"])
296 
297  # generator level analysis
298  from AsgAnalysisAlgorithms.AsgAnalysisConfig import GeneratorAnalysisBlock
299  self.addAlgConfigBlock(algName="GeneratorLevelAnalysis", alg=GeneratorAnalysisBlock)
300 
301  # pT/Eta Selection
302  from AsgAnalysisAlgorithms.AsgAnalysisConfig import PtEtaSelectionBlock
303  self.addAlgConfigBlock(algName="PtEtaSelection", alg=PtEtaSelectionBlock,
304  defaults={'selectionName': ''},
305  superBlocks=[self.ROOTNAME, "Jets", "Electrons", "Photons", "Muons", "TauJets"])
306 
307  # met
308  from MetAnalysisAlgorithms.MetAnalysisConfig import MetAnalysisConfig
309  self.addAlgConfigBlock(algName="MissingET", alg=MetAnalysisConfig)
310 
311  # overlap removal
312  from AsgAnalysisAlgorithms.OverlapAnalysisConfig import OverlapAnalysisConfig
313  self.addAlgConfigBlock(algName="OverlapRemoval", alg=OverlapAnalysisConfig,
314  defaults={'configName': 'OverlapRemoval'})
315 
316  # object-based cutflow
317  from AsgAnalysisAlgorithms.AsgAnalysisConfig import ObjectCutFlowBlock
318  self.addAlgConfigBlock(algName='ObjectCutFlow', alg=ObjectCutFlowBlock)
319 
320  # event selection
321  from EventSelectionAlgorithms.EventSelectionConfig import makeMultipleEventSelectionConfigs
322  self.addAlgConfigBlock(algName='EventSelection', alg=makeMultipleEventSelectionConfigs)
323 
324  # event-based cutflow
325  from AsgAnalysisAlgorithms.AsgAnalysisConfig import EventCutFlowBlock
326  self.addAlgConfigBlock(algName='EventCutFlow', alg=EventCutFlowBlock,
327  defaults={'containerName': 'EventInfo', 'selectionName': ''})
328 
329  # bootstraps
330  from AsgAnalysisAlgorithms.BootstrapGeneratorConfig import BootstrapGeneratorConfig
331  self.addAlgConfigBlock(algName='Bootstraps', alg=BootstrapGeneratorConfig)
332 
333  # per-event scale factor calculation
334  from AsgAnalysisAlgorithms.AsgAnalysisConfig import PerEventSFBlock
335  self.addAlgConfigBlock(algName='PerEventSF', alg=PerEventSFBlock)
336 
337  # thinning
338  from AsgAnalysisAlgorithms.AsgAnalysisConfig import OutputThinningBlock
339  self.addAlgConfigBlock(algName="Thinning", alg=OutputThinningBlock,
340  defaults={'configName': 'Thinning'})
341 
342  # selection decorations
343  from AsgAnalysisAlgorithms.AsgAnalysisConfig import SelectionDecorationBlock
344  self.addAlgConfigBlock(algName='SelectionDecoration',
345  alg=SelectionDecorationBlock)
346 
347  # di-tau mass calculator
348  from TauAnalysisAlgorithms.DiTauMassConfig import DiTauMassBlock
349  self.addAlgConfigBlock(algName="DiTauMMC", alg=DiTauMassBlock)
350 
351  # output
352  from AsgAnalysisAlgorithms.OutputAnalysisConfig import OutputAnalysisConfig
353  self.addAlgConfigBlock(algName="Output", alg=OutputAnalysisConfig,
354  defaults={'configName': 'Output'})
355 
356  # IOStats printouts
357  from AsgAnalysisAlgorithms.AsgAnalysisConfig import IOStatsBlock
358  self.addAlgConfigBlock(algName="IOStats", alg=IOStatsBlock)
359 
360  return
python.ConfigFactory.ConfigFactory.printAlgs
def printAlgs(self, printOpts=True)
Definition: ConfigFactory.py:165
python.ConfigFactory.ConfigFactory
Definition: ConfigFactory.py:108
python.ConfigFactory.getDefaultArgs
def getDefaultArgs(func)
Definition: ConfigFactory.py:25
python.ConfigFactory.FactoryBlock.__init__
def __init__(self, alg, algName, options, defaults, subAlgs=None)
Definition: ConfigFactory.py:49
python.ConfigFactory.getFuncArgs
def getFuncArgs(func)
Definition: ConfigFactory.py:35
python.ConfigFactory.FactoryBlock
Definition: ConfigFactory.py:46
python.ConfigFactory.FactoryBlock.defaults
defaults
Definition: ConfigFactory.py:53
python.ConfigFactory.ConfigFactory.addDefaultAlgs
def addDefaultAlgs(self)
Definition: ConfigFactory.py:212
python.ConfigFactory.FactoryBlock.makeConfig
def makeConfig(self, funcOptions)
Definition: ConfigFactory.py:60
python.ConfigFactory.ConfigFactory.makeConfig
def makeConfig(self, name, **kwargs)
Definition: ConfigFactory.py:195
python.ConfigFactory.FactoryBlock.alg
alg
Definition: ConfigFactory.py:50
python.ConfigFactory.ConfigFactory._order
_order
Definition: ConfigFactory.py:115
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.ConfigFactory.FactoryBlock.options
options
Definition: ConfigFactory.py:52
python.ConfigFactory.ConfigFactory._algs
_algs
Definition: ConfigFactory.py:114
python.ConfigFactory.ConfigFactory.addAlgConfigBlock
def addAlgConfigBlock(self, algName, alg, defaults=None, pos=None, superBlocks=None)
Definition: ConfigFactory.py:120
python.ConfigFactory.ConfigFactory.__init__
def __init__(self, addDefaultBlocks=True)
Definition: ConfigFactory.py:112
python.ConfigFactory.ConfigFactory.ROOTNAME
ROOTNAME
Definition: ConfigFactory.py:113
python.ConfigFactory.FactoryBlock.subAlgs
subAlgs
Definition: ConfigFactory.py:55
python.ConfigFactory.FactoryBlock.algName
algName
Definition: ConfigFactory.py:51