Tools and tips

Last update: 06 Dec 2023 [History] [Edit]

Browsing and comparing configurations

The configuration stored in ComponentAccumulator can be saved in the file like this:

acc = ComponentAccumulator()
...
# at the end of config file
with open("config.pkl", "wb") as f:
    acc.store(f)

or from a postExec like

--postExec 'with open("config.pkl", "wb") as f: cfg.store(f)'

The format of the file is binary however it can be browsed with the confTool.py and iconfTool tools:

> confTool.py -p config.pkl
# or for a more interactive and visual browsing
> iconfTool -i config.pkl

The confTool.py has multiple options to constrain or expand the amount of details in the output (see -h option). For action available in the iconfTool hit the keyword h.

Both tools can be used for comparing the configuration.

> confTool.py --diff old-config.pkl config.pkl

The output will indicate missing as well as different settings.

For comparisons with old configuration the athena option --config-only="old-config.pkl" can be added to Athena command line. With this the job does not actually run but the configuration is saved in the file that can be browsed or compared as above.

The confTool.py has several handy options to suppress the differences that are false-positives due to the differences in how old & new configuration systems work. They are:

  • --ignoreDefaults - that ignores those settings that are set in python yet are identical to the defaults from c++.
  • --shortenDefaultComponents - that changes component names of the form AComp/AComp to AComp,
  • --knownDifferencesFile - loads the file that lists acceptable differences in the format:
    • Alg.ToolA.ToolB.settingX = Value - signifying that config for the new settingX has value and old one does not,
    • Alg.ToolA.ToolB.settingX Value = - the opposite,
    • Alg.ToolA.ToolB.settingX Value1 = Value2 - signifying that the Value1 is replaced by Value2,
    • Alg.ToolA.ToolB.settingX = - indicating that differences in this setting should be disregarded irrespective of the values in old and new config file.
      An example listing know differences between q431 and new JO reconstruction can be found here
  • --renameCompsFile - loads file with directives altering component names so that they can be compared. Example can be found here.

Discovering properties of the component

The component properties are also inherited from base classes. A quickest way to discover all of them is by instantiating the python configuration class and list the class description as in the following example:

# in the terminal with atlas s/w set-up
> python
>>> from AthenaConfiguration.ComponentFactory import CompFactory
>>> algClass = CompFactory.HelloAlg # an example algorithm in this case
>>> help(algClass)

This will result in the output like below with the details of all properties enlisted:

Help on class HelloAlg in module GaudiConfig2.Configurables:

class HelloAlg(GaudiConfig2._configurables.Configurable)
 |  HelloAlg(name=None, **kwargs)
 |
 |  Properties
 |  ----------
 |  - ExtraInputs: std::unordered_set<DataObjID,DataObjID_Hasher,std::equal_to<DataObjID>,std::allocator<DataObjID> > ([])
 |     [DataHandleHolderBase<PropertyHolder<CommonMessaging<implements<IAlgorithm,IDataHandleHolder,IProperty,IStateful> > > >]
 |
 |  - ExtraOutputs: std::unordered_set<DataObjID,DataObjID_Hasher,std::equal_to<DataObjID>,std::allocator<DataObjID> > ([])
 |     [DataHandleHolderBase<PropertyHolder<CommonMessaging<implements<IAlgorithm,IDataHandleHolder,IProperty,IStateful> > > >]
 |
 |  - OutputLevel: int (0)
 |    output level [Gaudi::Algorithm]
 |
 |  - Enable: bool (True)
 |    should the algorithm be executed or not [Gaudi::Algorithm]
 |
 |  - ErrorMax: unsigned int (1)
 |    [[deprecated]] max number of errors [Gaudi::Algorithm]
 .....

In the configuration objects (not classes!) can be queried for:

  • properties that are already set with comp._properties that returns dictionary with property name and value,
  • all properties with comp._descriptors returning dictionary with property name and descriptor object holding info about C++ type, default value and semantics,
  • complete component class name/instance name comp.getFullJobOptName() (e.g. MyNameSpace::MyTool/ToolA),
  • short name (without the class name) comp.name
  • component type comp.getGaudiType() that returns one of strings “Algorithm”, “Tool” or “Service”.

Debugging the configuration process

Sometimes it is necessary to interrupt the configuration process and be able to enter interactive mode somewhere deep in the calls stack. The code python module can be used for this.

In place where you intend the configuration process to be interrupted add the following:

import code; code.interact(banner="me debugging >>> ", local=locals())

With this the configuration process will be interrupted and a control given to you. You can inspect local scope variables as needed. With Control-D that session will be ended and configuration process resumed.

Another option could be to use the python debugger pdb. Where you want to abort the configuration, add the following:

import pdb; pdb.set_trace()

Finding more about the conflicts

When using the CA the merge conflicts can arise. Since it is sometimes difficult to discover how the conflicting component is configured in such situation. Therefore the collection of context information can be enabled by setting: ComponentAccumulator.debugMode="trackCA trackEventAlgo ..." in the main script. (Collection of the context information slows down the configuration process and therefore it can be turned on selectively for specific components. Look up for all keywords in the CA implementation.) With this, the merging conflict exception is extended by an information as to where the conflicting components are defined. That may look like this:

Traceback (most recent call last):
 File "/srv/build/x86_64-centos7-gcc8-opt/python/AthenaConfiguration/ComponentAccumulatorTest.py", line 98, in test_conflict_in_cond_alg
    acc.addCondAlgo(ExampleAlg1("Cond1", MyInt=8))
.....
ValueError: conflicting settings for property MyInt of ExampleAlg1: cannot merge values 8 and 7
with the context
 Adding Conditions Algorithm to the ComponentAccumulator that has instance of it already added in this context
 >>> runpy.py:193(_run_module_as_main) case.py:624(run) ComponentAccumulatorTest.py:57(setUp)

The last line contains an abbreviated sequence of calls that lead to the creation of the component conflicting with the one attempted to be added.

If the context collection is not enabled the hint message like below is displayed:

...
ValueError: conflicting settings for property MyInt of Cond1: cannot merge values 8 and 7
with the context
 Unknown (enable it with ComponentAccumulator.debugMode)

Profiling and optimising configuration

The configuration process can take a significant amount of time and it can be useful to profile it to find the bottlenecks.

The cProfile module can be used for this. The following example shows how to profile the configuration process of the MuonCombinedReconstruction job options:

python -m cProfile -o profile -m MuonCombinedConfig.MuonCombinedReconstructionConfig --threads=1

If you want to profile a more complex transform, the easiest approach is probably to modify the runargs

# Import skeleton and execute it
from RecJobTransforms.RAWtoALL_Skeleton import fromRunArgs
import cProfile
cProfile.run('fromRunArgs(runArgs)', filename='RAWtoALL.prof')

The profiling output can be analysed with the snakeviz tool (see example below).

Once you have identified the bottlenecks, you can try to optimise the configuration process. For example, an AccumulatorCache class can be used to cache the results of the configuration functions (this was discussed earlier in “Caching of configuration results”).

!57202 is an example of a MR showing some snakeviz screenshots, and optimisations using AccumulatorCache.

Setting properties while debugging

The CA has functionality to search for components and set their properties independently of where (how deep in tool chains/ sequences) they are. This functionality is only available during debugging. I.e each time it is used results in an ERROR message in the log. This is not allowed in production. The usage is following:

#ca.foreach_component(wildcard).Property=value
#example: setting all extrapolators property 
ca.foreach_component("*/Extrapolator/*").UseMuonMatApproximation=False
#example: setting all HLT Hypothesis algorithms
ca.foreach_component("*/HLT/**/*").RuntimeValidation=False

The path to component is constructed as a UNIX path using sequence names, class name and instance name with fnmatch. For instance XTool of UsefulTool configured for algorithm YAlg of type UsefulAlg will have a path: AthMasterSeq/AthAlgEvtSeq/AthAllAlgSeq/AthAlgSeq/UsefulAlg/YAlg/UsefulTool/XTool.

To see which components were matched, look for PropSetterProxy in the configuration output, just before Welcome to ApplicationMgr. It will list the value, property, and full path of the component matched.