ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
PhysicsAnalysis
Algorithms
AnalysisAlgorithmsConfig
python
ConfigSequence.py
Go to the documentation of this file.
1
# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3
from
AnalysisAlgorithmsConfig.ConfigBlock
import
ConfigBlock
4
from
AnaAlgorithm.Logging
import
logging
5
logCPAlgCfgSeq = logging.getLogger(
'CPAlgCfgSeq'
)
6
7
from
functools
import
wraps
8
from
random
import
randrange
9
import
re
10
11
def
groupBlocks
(func):
12
"""
13
Decorates a configSequence or function with 'seq' as a
14
arguement.
15
16
Sets groupName to the name of the decorated funtion or
17
calss plus and integer for each ConfigBlock in the configSequence.
18
19
Blocks with the same groupName can be configured together.
20
"""
21
@wraps(func)
22
def
wrapper(**kwargs):
23
func(**kwargs)
24
groupName = f
"{func.__name__}_{randrange(10**8):08}"
25
for
block
in
kwargs[
'seq'
]:
26
block.setOptionValue(
'groupName'
, groupName)
27
return
wrapper
28
29
class
ConfigSequence
:
30
"""a sequence of ConfigBlock objects
31
32
This could in principle just be a simple python list, and maybe we
33
change it to that at some point (10 Mar 22). Having it as its own
34
class allows to implement some helper functions.
35
36
This implements an interface similar to ConfigBlock, but it
37
doesn't derive from it, as ConfigBlock will likely gain
38
functionality in the future that wouldn't work for a sequence (or
39
wouldn't work in the same way).
40
41
"""
42
43
def
__init__
(self) :
44
self.
_blocks
= []
45
46
47
def
append
(self, block) :
48
"""append a configuration block to the sequence"""
49
self.
_blocks
.append (block)
50
51
52
def
makeAlgs
(self, config) :
53
"""call makeAlgs() on all blocks
54
55
This will create the actual algorithm configurables based on
56
how the blocks are configured right now.
57
"""
58
for
block
in
self.
_blocks
:
59
if
not
block.isUsedForConfig(config):
60
continue
61
config.setAlgPostfix(block.instanceName())
62
block.checkExpertSettings (config)
63
block.makeAlgs (config)
64
config.setAlgPostfix(
''
)
# reset algPostfix after all blocks are configured
65
ConfigBlock.instance_counts.clear()
# reset for a possible next config run
66
67
68
def
applyConfigOverrides
(self, config):
69
"""
70
Apply any properties that were set in the block's
71
'propertyOverrides' option.
72
"""
73
for
block
in
self.
_blocks
:
74
if
not
block.isUsedForConfig(config):
75
continue
76
config.setAlgPostfix(block.instanceName())
77
block.applyConfigOverrides(config)
78
config.setAlgPostfix(
''
)
# reset algPostfix after all blocks are configured
79
80
def
reorderAlgs
(self):
81
"""
82
Check for blocks with dependencies.
83
84
If a block required another block that is not present, will
85
throw an error; Otherwise, will move block immediately after
86
required block. If dependency is not required, will move
87
after other block, if it is present.
88
89
Note: this implementation can only move blocks forward.
90
"""
91
def
moveBlock(blocks):
92
for
i, block
in
enumerate(blocks):
93
# the 'ignoreDependencies' option is added with a dep.
94
ignore = block.getOptionValue(
'ignoreDependencies'
)
95
if
block.hasDependencies():
96
depIdx = i
97
for
dep
in
block.getDependencies():
98
if
dep
in
ignore:
99
continue
100
# find dep with largest idx
101
if
dep
in
blocks:
102
lastIdx =
max
(index
for
index,value
in
enumerate(blocks)
if
value == dep.blockName)
103
if
lastIdx > depIdx:
104
depIdx = lastIdx
105
elif
dep.required:
106
raise
ValueError(f
"{dep} block is required"
107
f
" for {block} but was not found."
)
108
# check to see if block is already infront of deps
109
if
depIdx > i:
110
logCPAlgCfgSeq.info(f
"Moving {block} after {blocks[depIdx]}"
)
111
# depIdx > i so after pop, depIdx -= 1 -> depIdx is after dep
112
blocks.insert(depIdx, blocks.pop(i))
113
return
False
114
# nothing to move
115
return
True
116
MAXTRIES = 1000
117
for
_
in
range(MAXTRIES):
118
if
moveBlock(self.
_blocks
):
119
# sorted
120
break
121
else
:
122
raise
Exception(
"Could not order blocks based on dependencies"
123
f
" in {MAXTRIES} moves."
)
124
125
126
def
fullConfigure
(self, config) :
127
"""do the full configuration on this sequence
128
129
This sequence needs to be the only sequence, i.e. it needs to
130
contain all the blocks that will be configured, as it will
131
perform all configuration steps at once.
132
"""
133
for
block
in
self.
_blocks
:
134
if
re.compile (
'^[_a-zA-Z0-9]*$'
).match (block.instanceName())
is
None
:
135
raise
ValueError (f
'invalid block instance name: {block.instanceName()} for {block.factoryName()}'
)
136
137
self.
reorderAlgs
()
138
self.
makeAlgs
(config)
139
self.
applyConfigOverrides
(config)
140
config.renameFinalContainers()
141
142
143
def
setOptionValue
(self, name, value, **kwargs) :
144
"""set the given option on the sequence
145
146
The name should generally be of the form
147
"groupName.optionName" to identify what group the option
148
belongs to.
149
150
For simplicity I also allow a ".optionName" here, which will
151
then set the property in the last group added. That makes it
152
fairly straightforward to add new blocks, set options on them,
153
and then move on to the next blocks. Please note that this
154
mechanism ought to be viewed as strictly as a temporary
155
convenience, and this short cut may go away once better
156
alternatives are available.
157
158
WARNING: The backend to option handling is slated to be
159
replaced at some point. This particular function may change
160
behavior, interface or be removed/replaced entirely.
161
"""
162
names = name.split(
'.'
)
163
# <optionName>
164
optionName = names.pop(-1)
165
# <groupName>.<optionName>, or
166
# .<optionName> (backwards compatability)
167
groupName = names.pop(0)
if
names
else
''
168
if
names:
169
raise
ValueError(f
'Option name can be either <groupName>.<optionName>'
170
f
' or <optionName> not {name}'
)
171
blocks = self.
_blocks
172
# check if last block added has an instance name
173
if
not
groupName:
174
groupName = blocks[-1].getOptionValue(
'groupName'
)
175
if
groupName:
176
used =
False
177
# set optionName for all blocks with groupName
178
for
block
in
blocks:
179
if
( block.getOptionValue(
'groupName'
) == groupName
180
and
block.hasOption(optionName) ):
181
block.setOptionValue (optionName, value, **kwargs)
182
used =
True
183
if
not
used:
184
raise
ValueError(f
'{optionName} not found in blocks with '
185
f
'group name {groupName}'
)
186
else
:
187
# set opyion for last added block
188
blocks[-1].setOptionValue (optionName, value, **kwargs)
189
190
191
def
printOptions
(self, verbose=False):
192
"""
193
Prints options and their values for each config block in a config sequence
194
"""
195
for
config
in
self:
196
logCPAlgCfgSeq.info(config.__class__.__name__)
197
config.printOptions(verbose=verbose)
198
199
200
def
getOptions
(self):
201
"""get information on options for last block in sequence"""
202
# get groupName for last added block
203
groupName = self.
_blocks
[-1].getOptionValue(
'groupName'
)
204
blocks = [self.
_blocks
[-1]]
205
# get all blocks with the same groupName
206
if
groupName:
207
for
block
in
self.
_blocks
[:-1]:
208
if
block.getOptionValue(
'groupName'
) == groupName:
209
blocks.append(block)
210
options = []
211
# get options for all blocks with same groupName
212
for
block
in
blocks:
213
for
name, o
in
block.getOptions().items():
214
val = getattr(block, name)
215
valDefault = o.default
216
valType = o.type
217
valRequired = o.required
218
noneAction = o.noneAction
219
options.append({
'name'
: name,
'defaultValue'
: valDefault,
220
'type'
: valType,
'required'
: valRequired,
221
'noneAction'
: noneAction,
'value'
: val})
222
return
options
223
224
225
def
setOptions
(self, options):
226
"""Set options for a ConfigBlock"""
227
algOptions = self.
getOptions
()
228
for
opt
in
algOptions:
229
name = opt[
'name'
]
230
if
name
in
options:
231
self.
setOptionValue
(f
'.{name}'
, options[name])
232
logCPAlgCfgSeq.debug(f
" {name}: {options[name]}"
)
233
else
:
234
if
opt[
'required'
]:
235
raise
ValueError(f
'{name} is required but not included in config'
)
236
# add default used to config
237
defaultVal = opt[
'value'
]
if
opt[
'value'
]
else
opt[
'defaultValue'
]
238
# do not overwright groupName unless set by user
239
if
name !=
'groupName'
:
240
options[name] = defaultVal
241
logCPAlgCfgSeq.debug(f
" {name}: {defaultVal}"
)
242
# refresh and return options
243
return
self.
getOptions
()
244
245
246
def
groupBlocks
(self, groupName=''):
247
"""
248
Assigns all blocks in configSequence groupName. If no name is
249
provided, the name is set to group_ plus an integer.
250
251
Blocks with the same groupName can be configured together.
252
"""
253
if
not
groupName:
254
groupName = f
"group_{randrange(10**8):08}"
255
for
block
in
self.
_blocks
:
256
block.setOptionValue(
'groupName'
, groupName)
257
258
259
def
setFactoryName
(self, factoryName):
260
"""
261
Set the factory name for all blocks in the sequence.
262
263
This is used to set a common factory name for all blocks, which
264
can be useful for debugging or logging purposes.
265
"""
266
if
len(self.
_blocks
) == 1:
267
self.
_blocks
[0].
setFactoryName
(factoryName)
268
else
:
269
# append [index] to factoryName for each block
270
for
index, block
in
enumerate(self.
_blocks
):
271
block.setFactoryName(f
"{factoryName}[{index}:{block.__class__.__name__}]"
)
272
273
def
__iadd__
( self, sequence, index = None ):
274
"""Add another sequence to this one
275
276
This function is used to add another sequence to this sequence
277
using the '+=' operator.
278
"""
279
# Check that the received object is of the right type:
280
if
not
isinstance( sequence, ConfigSequence ):
281
raise
TypeError(
'The received object is not of type ConfigSequence'
)
282
283
for
block
in
sequence._blocks :
284
self.
_blocks
.append (block)
285
286
# Return the modified object:
287
return
self
288
289
290
def
__iter__
( self ):
291
"""Create an iterator over all the configurations in this sequence
292
293
This is to allow for a Python-like iteration over all
294
configuration blocks that are part of the sequence.
295
"""
296
# Create the iterator to process the internal list of algorithms:
297
return
self.
_blocks
.
__iter__
()
max
#define max(a, b)
Definition
cfImp.cxx:41
python.ConfigSequence.ConfigSequence
Definition
ConfigSequence.py:29
python.ConfigSequence.ConfigSequence.__iter__
__iter__(self)
Definition
ConfigSequence.py:290
python.ConfigSequence.ConfigSequence.groupBlocks
groupBlocks(self, groupName='')
Definition
ConfigSequence.py:246
python.ConfigSequence.ConfigSequence.setOptionValue
setOptionValue(self, name, value, **kwargs)
Definition
ConfigSequence.py:143
python.ConfigSequence.ConfigSequence._blocks
list _blocks
Definition
ConfigSequence.py:44
python.ConfigSequence.ConfigSequence.append
append(self, block)
Definition
ConfigSequence.py:47
python.ConfigSequence.ConfigSequence.reorderAlgs
reorderAlgs(self)
Definition
ConfigSequence.py:80
python.ConfigSequence.ConfigSequence.__iadd__
__iadd__(self, sequence, index=None)
Definition
ConfigSequence.py:273
python.ConfigSequence.ConfigSequence.__init__
__init__(self)
Definition
ConfigSequence.py:43
python.ConfigSequence.ConfigSequence.makeAlgs
makeAlgs(self, config)
Definition
ConfigSequence.py:52
python.ConfigSequence.ConfigSequence.fullConfigure
fullConfigure(self, config)
Definition
ConfigSequence.py:126
python.ConfigSequence.ConfigSequence.setFactoryName
setFactoryName(self, factoryName)
Definition
ConfigSequence.py:259
python.ConfigSequence.ConfigSequence.setOptions
setOptions(self, options)
Definition
ConfigSequence.py:225
python.ConfigSequence.ConfigSequence.printOptions
printOptions(self, verbose=False)
Definition
ConfigSequence.py:191
python.ConfigSequence.ConfigSequence.getOptions
getOptions(self)
Definition
ConfigSequence.py:200
python.ConfigSequence.ConfigSequence.applyConfigOverrides
applyConfigOverrides(self, config)
Definition
ConfigSequence.py:68
python.ConfigSequence.groupBlocks
groupBlocks(func)
Definition
ConfigSequence.py:11
Generated on
for ATLAS Offline Software by
1.17.0