ATLAS Offline Software
Loading...
Searching...
No Matches
ConfigFactory.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 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
18import inspect
19from AnalysisAlgorithmsConfig.ConfigSequence import ConfigSequence
20
21from AnaAlgorithm.Logging import logging
22logCPAlgCfgFactory = logging.getLogger('CPAlgCfgFactory')
23
24
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
35def 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
47 """
48 """
49 def __init__(self, alg, factoryName, algName, options, defaults, subAlgs=None):
50 self.alg = alg
51 self.factoryName = factoryName
52 self.algName = algName
53 self.options = options
54 self.defaults = defaults
55 if subAlgs is None:
56 self.subAlgs = {}
57 else:
58 self.subAlgs = subAlgs
59
60
61 def makeConfig(self, funcOptions):
62 """
63 Parameters
64 ----------
65 funcName: str
66 name associated with the algorithm. This name must have been added to the
67 list of available algorithms
68 funcOptions: dict
69 dictionary containing options for the algorithm read from the YAML file
70
71 Returns
72 -------
73 configSequence
74 """
75 configSeq = ConfigSequence()
76
77 func = self.alg
78 funcName = self.algName
79 funcDefaults = getDefaultArgs(func)
80 defaults = self.defaults
81
82 args = {}
83 # loop over all options for the function
84 for arg in getFuncArgs(func):
85 # supplied from config file
86 if arg in funcOptions:
87 args[arg] = funcOptions[arg]
88 # defaults set in function def
89 elif arg in funcDefaults:
90 args[arg] = funcDefaults[arg]
91 # defaults provided when func was added
92 elif defaults is not None and arg in defaults:
93 args[arg] = defaults[arg]
94 elif arg == 'seq':
95 # 'seq' should be first arg of func (not needed for class)
96 args[arg] = configSeq
97 elif arg == 'kwargs':
98 # cannot handle arbitrary parameters
99 continue
100 else:
101 raise ValueError(f"{arg} is required for {funcName}")
102 if inspect.isclass(func):
103 configSeq.append(func(**args))
104 else:
105 func(**args)
106 configSeq.setFactoryName(self.factoryName)
107 return configSeq, args.keys()
108
109
111 """This class provides a configuration manager that is intended to allow the user to:
112 - define and configure functions that return an algSequence(?) object
113 """
114 def __init__(self, addDefaultBlocks=True):
115 self.ROOTNAME = 'root' # constant
116 self._algs = {}
117 self._order = {self.ROOTNAME: []}
118 if addDefaultBlocks:
119 self.addDefaultAlgs()
120
121
122 def addAlgConfigBlock(self, algName, alg, defaults=None, pos=None, superBlocks=None):
123 """Add class to list of available algorithms"""
124 if not callable(alg):
125 raise ValueError(f"{algName} is not a callable.")
126 opts = getFuncArgs(alg)
127
128 if superBlocks is None:
129 superBlocks = [self.ROOTNAME]
130 elif not isinstance(superBlocks, list):
131 superBlocks = [superBlocks]
132
133 # add new alg block to subAlgs dict of super block
134 for block in superBlocks:
135 if block not in self._order:
136 self._order[block] = []
137 order = self._order[block]
138
139 if block == self.ROOTNAME:
140 algs = self._algs
141 else:
142 if block not in self._algs:
143 raise ValueError(f"{block} not added")
144 algs = self._algs[block].subAlgs
145
146 if algName in algs:
147 raise ValueError(f"{algName} has already been added.")
148
149 if block != self.ROOTNAME:
150 factoryName = f"{block}.{algName}"
151 else:
152 factoryName = algName
153
154 # create FactoryBlock with alg information
155 algs[algName] = FactoryBlock(
156 alg=alg,
157 factoryName=factoryName,
158 algName=algName,
159 options=opts,
160 defaults=defaults,
161 subAlgs={}
162 )
163 # insert into order (list)
164 if pos is None:
165 order.append(algName)
166 elif pos in order:
167 order.insert(order.index(pos), algName)
168 else:
169 raise ValueError(f"{pos} does not exit in already added config blocks")
170 return
171
172
173 def printAlgs(self, printOpts=True):
174 """Prints algorithms exposed to configuration"""
175 printed = [] # some subblocks exist for multiple superblocks
176 def printAlg(algs):
177 for alg, algInfo in algs.items():
178 algName = algInfo.alg.__name__
179 algOptions = algInfo.options
180 if algName not in printed:
181 printed.append(algName)
182 logCPAlgCfgFactory.info(f"\033[4m{alg}\033[0m -> \033[4m{algName}\033[0m")
183 if printOpts:
184 try:
185 if inspect.isclass(algInfo.alg):
186 # block
187 algInfo.alg().printOptions(verbose=printOpts)
188 else:
189 # make function
190 seq = ConfigSequence()
191 algInfo.alg(seq=seq)
192 seq.printOptions(verbose=printOpts)
193 except Exception:
194 # either a TypeError or something else due to missing args
195 # try to print something for casses with required args
196 for opt in algOptions:
197 logCPAlgCfgFactory.info(f" {opt}")
198 printAlg(algInfo.subAlgs)
199 printAlg(self._algs)
200 return
201
202
203 def makeConfig(self, name, **kwargs):
204 """
205 Returns:
206 configSeq: configSequence object
207 """
208 try:
209 if '.' in name:
210 algContext, algName = name.split('.')
211 block = self._algs[algContext].subAlgs[algName]
212 else:
213 block = self._algs[name]
214 except KeyError:
215 raise ValueError(f"{name} config block not found. Make sure context is correct.")
216 # Optional **kwargs are in the process of being retired. While the process is not fully complete
217 # we still need to allow them to be passed. However, for blocks where they have already been retired
218 # we want to raise an error so users don't experience undesirable behaviour where their extra options
219 # are being ignored, or run into related cryptic crashes.
220 already_fixed_blocks = {
221 'Electrons','Photons','Muons','TauJets','DiTauJets','MissingET','FlavourTagging','FlavourTaggingEventSF','XbbTagging',
222 'InDetTracks','KLFitter','PtEtaSelection','ObjectCutFlow','EventCutFlow','Thinning',
223 'IFFClassification','MCTCClassification','PerEventSF','SelectionDecoration','SystObjectLink'}
224 if kwargs and name.split('.')[-1] in already_fixed_blocks:
225 raise ValueError(f"Config block '{name}' no longer accepts **kwargs. Use config.setOptionValue('option', value) instead!")
226 configSeq, _ = block.makeConfig(kwargs)
227 return configSeq
228
229
230 def addDefaultAlgs(self):
231 """add algorithms and options"""
232
233 # CommonServices
234 from AsgAnalysisAlgorithms.AsgAnalysisConfig import CommonServices
235 self.addAlgConfigBlock(algName="CommonServices", alg=CommonServices)
236
237 # pileup reweighting
238 from AsgAnalysisAlgorithms.AsgAnalysisConfig import PileupReweightingBlock
239 self.addAlgConfigBlock(algName="PileupReweighting", alg=PileupReweightingBlock)
240
241 # event cleaning
242 from AsgAnalysisAlgorithms.EventCleaningConfig import EventCleaningBlock
243 self.addAlgConfigBlock(algName="EventCleaning", alg=EventCleaningBlock)
244
245 # trigger
246 from TriggerAnalysisAlgorithms.TriggerAnalysisConfig import Trigger
247 self.addAlgConfigBlock(algName="Trigger", alg=Trigger)
248 from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import TriggerAnalysisSFBlock
249 self.addAlgConfigBlock(algName="TriggerMatching", alg=TriggerAnalysisSFBlock)
250
251 # jets
252 from JetAnalysisAlgorithms.JetAnalysisConfig import Jets
253 self.addAlgConfigBlock(algName="Jets", alg=Jets)
254 from JetAnalysisAlgorithms.JetAnalysisConfig import JvtWorkingPoint
255 self.addAlgConfigBlock(algName="JVTWorkingPoint", alg=JvtWorkingPoint,
256 superBlocks="Jets")
257 from JetAnalysisAlgorithms.JetAnalysisConfig import FJvtWorkingPoint
258 self.addAlgConfigBlock(algName="FJVTWorkingPoint", alg=FJvtWorkingPoint,
259 superBlocks="Jets")
260 from JetAnalysisAlgorithms.JetJvtAnalysisConfig import JetJvtAnalysisConfig
261 self.addAlgConfigBlock(algName="JVT", alg=JetJvtAnalysisConfig,
262 superBlocks="Jets")
263 from JetAnalysisAlgorithms.BJetCalibAnalysisConfig import BJetCalibAnalysisConfig
264 self.addAlgConfigBlock(algName="BJetCalib", alg=BJetCalibAnalysisConfig,
265 superBlocks="Jets")
266 from JetAnalysisAlgorithms.JetTriggerAnalysisConfig import JetTriggerMatchingBlock
267 self.addAlgConfigBlock(algName="TriggerMatching", alg=JetTriggerMatchingBlock,
268 superBlocks="Jets")
269 from FTagAnalysisAlgorithms.FTagTrigMatchAnalysisConfig import FTagJetTrigMatchingBlock
270 self.addAlgConfigBlock(algName="FTagTriggerMatching", alg=FTagJetTrigMatchingBlock,
271 superBlocks="Jets")
272 from FTagAnalysisAlgorithms.FTagAnalysisConfig import FTagConfig
273 self.addAlgConfigBlock(algName="FlavourTagging", alg=FTagConfig,
274 defaults={'selectionName': ''},
275 superBlocks="Jets")
276 from JetAnalysisAlgorithms.JetUncertaintiesConfig import JetUncertaintiesConfig
277 self.addAlgConfigBlock(algName="Uncertainties", alg=JetUncertaintiesConfig,
278 superBlocks="Jets")
279 from FTagAnalysisAlgorithms.XbbAnalysisConfig import XbbConfig
280 self.addAlgConfigBlock(algName="XbbTagging", alg=XbbConfig,
281 superBlocks="Jets")
282 from FTagAnalysisAlgorithms.FTagSFAnalysisConfig import FlavourTaggingEventSF
283 self.addAlgConfigBlock(algName="FlavourTaggingEventSF",
284 alg=FlavourTaggingEventSF,
285 defaults={'selectionName': ''},
286 superBlocks="Jets")
287
288 # muons
289 from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonCalibration
290 self.addAlgConfigBlock(algName="Muons", alg=MuonCalibration)
291 from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonWorkingPoint
292 self.addAlgConfigBlock(algName="WorkingPoint", alg=MuonWorkingPoint,
293 superBlocks="Muons")
294 from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonTriggerAnalysisSFBlock
295 self.addAlgConfigBlock(algName="TriggerSF", alg=MuonTriggerAnalysisSFBlock,
296 superBlocks="Muons")
297 from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonLRTMergedConfig
298 self.addAlgConfigBlock(algName="LRTMerging", alg=MuonLRTMergedConfig,
299 superBlocks="Muons")
300 from MuonAnalysisAlgorithms.MuonAnalysisConfig import MuonContainerMergingConfig
301 self.addAlgConfigBlock(algName="ContainerMerging", alg=MuonContainerMergingConfig,
302 superBlocks="Muons")
303
304 # electrons
305 from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronCalibration
306 self.addAlgConfigBlock(algName="Electrons", alg=ElectronCalibration)
307 from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronWorkingPoint
308 self.addAlgConfigBlock(algName="WorkingPoint", alg=ElectronWorkingPoint,
309 superBlocks="Electrons")
310 from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronLRTMergedConfig
311 self.addAlgConfigBlock(algName="LRTMerging", alg=ElectronLRTMergedConfig,
312 superBlocks="Electrons")
313 from EgammaAnalysisAlgorithms.ElectronAnalysisConfig import ElectronTriggerAnalysisSFBlock
314 self.addAlgConfigBlock(algName="TriggerSF", alg=ElectronTriggerAnalysisSFBlock,
315 superBlocks="Electrons")
316
317 # photons
318 from EgammaAnalysisAlgorithms.PhotonAnalysisConfig import PhotonCalibrationConfig
319 self.addAlgConfigBlock(algName="Photons", alg=PhotonCalibrationConfig)
320 from EgammaAnalysisAlgorithms.PhotonAnalysisConfig import PhotonWorkingPoint
321 self.addAlgConfigBlock(algName="WorkingPoint", alg=PhotonWorkingPoint,
322 superBlocks="Photons")
323 from EgammaAnalysisAlgorithms.PhotonExtraVariablesConfig import PhotonExtraVariablesBlock
324 self.addAlgConfigBlock(algName="ExtraVariables", alg=PhotonExtraVariablesBlock,
325 superBlocks="Photons")
326
327 # tauJets
328 from TauAnalysisAlgorithms.TauAnalysisConfig import TauCalibrationConfig
329 self.addAlgConfigBlock(algName="TauJets", alg=TauCalibrationConfig)
330 from TauAnalysisAlgorithms.TauAnalysisConfig import TauWorkingPoint
331 self.addAlgConfigBlock(algName="WorkingPoint", alg=TauWorkingPoint,
332 superBlocks="TauJets")
333 from TauAnalysisAlgorithms.TauAnalysisConfig import TauTriggerAnalysisSFBlock
334 self.addAlgConfigBlock(algName="TriggerSF", alg=TauTriggerAnalysisSFBlock,
335 superBlocks="TauJets")
336
337 # diTauJets
338 from TauAnalysisAlgorithms.DiTauAnalysisConfig import DiTauCalibrationConfig
339 self.addAlgConfigBlock(algName="DiTauJets", alg=DiTauCalibrationConfig)
340 from TauAnalysisAlgorithms.DiTauAnalysisConfig import DiTauWorkingPoint
341 self.addAlgConfigBlock(algName="WorkingPoint", alg=DiTauWorkingPoint,
342 superBlocks="DiTauJets")
343
344 # tracks
345 from TrackingAnalysisAlgorithms.TrackingAnalysisConfig import InDetTrackCalibrationConfig
346 self.addAlgConfigBlock(algName="InDetTracks", alg=InDetTrackCalibrationConfig)
347 from TrackingAnalysisAlgorithms.TrackingAnalysisConfig import InDetTrackWorkingPointConfig
348 self.addAlgConfigBlock(algName="WorkingPoint", alg=InDetTrackWorkingPointConfig,
349 superBlocks="InDetTracks")
350
351 # SystObjectLink
352 from AsgAnalysisAlgorithms.SystObjectLinkConfig import SystObjectLinkBlock
353 self.addAlgConfigBlock(algName="SystObjectLink", alg=SystObjectLinkBlock,
354 superBlocks=[self.ROOTNAME, "Jets", "Electrons", "Photons", "Muons", "TauJets"])
355
356 # Particle-level truth algorithms
357 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelElectronsConfig import ParticleLevelElectronsBlock
358 self.addAlgConfigBlock(algName="PL_Electrons", alg=ParticleLevelElectronsBlock)
359 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelMuonsConfig import ParticleLevelMuonsBlock
360 self.addAlgConfigBlock(algName="PL_Muons", alg=ParticleLevelMuonsBlock)
361 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelNeutrinosConfig import ParticleLevelNeutrinosBlock
362 self.addAlgConfigBlock(algName="PL_Neutrinos", alg=ParticleLevelNeutrinosBlock)
363 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelJetsConfig import ParticleLevelJetsBlock
364 self.addAlgConfigBlock(algName="PL_Jets", alg=ParticleLevelJetsBlock)
365 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelTausConfig import ParticleLevelTausBlock
366 self.addAlgConfigBlock(algName="PL_Taus", alg=ParticleLevelTausBlock)
367 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelPhotonsConfig import ParticleLevelPhotonsBlock
368 self.addAlgConfigBlock(algName="PL_Photons", alg=ParticleLevelPhotonsBlock)
369 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelResonancesConfig import ParticleLevelResonancesBlock
370 self.addAlgConfigBlock(algName="PL_Resonances", alg=ParticleLevelResonancesBlock)
371 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelMissingETConfig import ParticleLevelMissingETBlock
372 self.addAlgConfigBlock(algName="PL_MissingET", alg=ParticleLevelMissingETBlock)
373 from TruthParticleLevelAnalysisAlgorithms.ParticleLevelOverlapRemovalConfig import ParticleLevelOverlapRemovalBlock
374 self.addAlgConfigBlock(algName="PL_OverlapRemoval", alg=ParticleLevelOverlapRemovalBlock)
375
376 # Parton-level truth algorithms
377 from TruthPartonLevelAnalysisAlgorithms.PartonHistoryConfig import PartonHistoryBlock
378 self.addAlgConfigBlock(algName="PartonHistory", alg=PartonHistoryBlock)
379
380 # IFF truth classification
381 from AsgAnalysisAlgorithms.AsgAnalysisConfig import IFFLeptonDecorationBlock
382 self.addAlgConfigBlock(algName="IFFClassification", alg=IFFLeptonDecorationBlock,
383 superBlocks=["Electrons", "Muons",
384 "PL_Electrons", "PL_Muons"])
385 # MCTC truth classification
386 from AsgAnalysisAlgorithms.AsgAnalysisConfig import MCTCLeptonDecorationBlock
387 self.addAlgConfigBlock(algName="MCTCClassification", alg=MCTCLeptonDecorationBlock,
388 superBlocks=["Electrons", "Muons", "TauJets",
389 "PL_Electrons", "PL_Muons", "PL_Taus"])
390
391 # pT/Eta Selection
392 from AsgAnalysisAlgorithms.AsgAnalysisConfig import PtEtaSelectionBlock
393 self.addAlgConfigBlock(algName="PtEtaSelection", alg=PtEtaSelectionBlock,
394 defaults={'selectionName': ''},
395 superBlocks=[self.ROOTNAME,
396 "Jets", "Electrons", "Photons", "Muons", "TauJets", "DiTauJets",
397 "PL_Jets", "PL_Electrons", "PL_Photons", "PL_Muons", "PL_Taus", "PL_Neutrinos", "PL_Resonances"])
398
399 # met
400 from MetAnalysisAlgorithms.MetAnalysisConfig import MetAnalysisConfig
401 self.addAlgConfigBlock(algName="MissingET", alg=MetAnalysisConfig)
402
403 # overlap removal
404 from AsgAnalysisAlgorithms.OverlapAnalysisConfig import OverlapAnalysisConfig
405 self.addAlgConfigBlock(algName="OverlapRemoval", alg=OverlapAnalysisConfig,
406 defaults={'configName': 'OverlapRemoval'})
407
408 # object-based cutflow
409 from AsgAnalysisAlgorithms.AsgAnalysisConfig import ObjectCutFlowBlock
410 self.addAlgConfigBlock(algName='ObjectCutFlow', alg=ObjectCutFlowBlock)
411
412 # jet reclustering
413 from JetAnalysisAlgorithms.JetReclusteringConfig import JetReclusteringBlock
414 self.addAlgConfigBlock(algName="JetReclustering", alg=JetReclusteringBlock)
415
416 # jet reclustering calibration
417 from JetAnalysisAlgorithms.ReclusteredJetCalibrationConfig import ReclusteredJetCalibrationBlock
418 self.addAlgConfigBlock(algName="ReclusteredJetCalibration", alg=ReclusteredJetCalibrationBlock)
419
420 # event selection
421 from EventSelectionAlgorithms.EventSelectionConfig import EventSelection
422 self.addAlgConfigBlock(algName='EventSelection', alg=EventSelection)
423
424 # event-based cutflow
425 from AsgAnalysisAlgorithms.AsgAnalysisConfig import EventCutFlowBlock
426 self.addAlgConfigBlock(algName='EventCutFlow', alg=EventCutFlowBlock)
427
428 # generator level analysis
429 from AsgAnalysisAlgorithms.AsgAnalysisConfig import GeneratorAnalysisBlock
430 self.addAlgConfigBlock(algName="GeneratorLevelAnalysis", alg=GeneratorAnalysisBlock)
431
432 # bootstraps
433 from AsgAnalysisAlgorithms.BootstrapGeneratorConfig import BootstrapGeneratorConfig
434 self.addAlgConfigBlock(algName='Bootstraps', alg=BootstrapGeneratorConfig)
435
436 # per-event scale factor calculation
437 from AsgAnalysisAlgorithms.AsgAnalysisConfig import PerEventSFBlock
438 self.addAlgConfigBlock(algName='PerEventSF', alg=PerEventSFBlock)
439
440 # per-event unified lepton scale factor calculation
441 from AsgAnalysisAlgorithms.LeptonSFCalculatorConfig import LeptonSFCalculatorBlock
442 self.addAlgConfigBlock(algName='LeptonSF', alg=LeptonSFCalculatorBlock)
443
444 # thinning
445 from AsgAnalysisAlgorithms.AsgAnalysisConfig import OutputThinningBlock
446 self.addAlgConfigBlock(algName="Thinning", alg=OutputThinningBlock,
447 defaults={'configName': 'Thinning'})
448
449 # selection decorations
450 from AsgAnalysisAlgorithms.AsgAnalysisConfig import SelectionDecorationBlock
451 self.addAlgConfigBlock(algName='SelectionDecoration',
452 alg=SelectionDecorationBlock)
453
454 # di-tau mass calculator
455 from TauAnalysisAlgorithms.DiTauMassConfig import DiTauMassBlock
456 self.addAlgConfigBlock(algName="DiTauMMC", alg=DiTauMassBlock)
457
458 # IFF fake background estimator
459 from AsgAnalysisAlgorithms.FakeBkgConfig import FakeBkgBlock
460 self.addAlgConfigBlock(algName='FakeBkgCalculator', alg=FakeBkgBlock)
461
462 # VGamma overlap removal
463 from AsgAnalysisAlgorithms.VGammaORConfig import VGammaORBlock
464 self.addAlgConfigBlock(algName='VGammaOR', alg=VGammaORBlock)
465
466 # output
467 from AsgAnalysisAlgorithms.OutputAnalysisConfig import OutputAnalysisConfig
468 self.addAlgConfigBlock(algName="Output", alg=OutputAnalysisConfig,
469 defaults={'configName': 'Output'})
470
471 # IOStats printouts
472 from AsgAnalysisAlgorithms.AsgAnalysisConfig import IOStatsBlock
473 self.addAlgConfigBlock(algName="IOStats", alg=IOStatsBlock)
474
475 # configuration printer
476 from AsgAnalysisAlgorithms.PrintToolConfigAlgConfig import PrintToolConfigAlgBlock
477 self.addAlgConfigBlock(algName="PrintConfiguration", alg=PrintToolConfigAlgBlock)
478
479 return
addAlgConfigBlock(self, algName, alg, defaults=None, pos=None, superBlocks=None)
__init__(self, addDefaultBlocks=True)
__init__(self, alg, factoryName, algName, options, defaults, subAlgs=None)