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