PyConf

Wrappers for defining safe yet flexible configuration for Gaudi applications.

PyConf is broadly split in two parts:

  1. Wrappers for creating data and control flow using composable Algorithm and Tool classes.

  2. Helpers for defining a configuration based on function call stacks that allow for changes to be configured deep down the stack.

To use algorithms and tools, don’t import them from Configurables but instead from PyConf.Algorithms and PyConf.Tools:

>>> from PyConf.Algorithms import MyGaudiAlgorithm
>>> from PyConf.Tools import MyTool

Any single Algorithm or Tool is immutable after instantiation, and hence the data flow that produces its inputs is known once the Algorithm or Tool has been created. The outputs of algorithms are modeled with DataHandle objects, which know the underlying C++ type of the data on the transient event store. When instantiating a component, the types of the provided inputs must be compatible with those expected by the component, otherwise an exception is raised.

The control flow of an application is defined by linking PyConf.control_flow.CompositeNode instances. Each node has a particular control flow mode, defined by PyConf.control_flow.NodeLogic.

Application data flow is automatically deduced by PyConf and Gaudi. The user only needs to define the control flow. For each algorithm in the control flow, the scheduler ensures that algorithm’s data dependencies are met.

Idiomatically, the configuration of individual algorithms is done within small helper functions that are configured via arguments:

>>> @configurable
... def gec(threshold=9750):
...     print('Applying threshold: {}'.format(threshold))

Rather than having to pass changes to default keyword arguments all the way down a call stack, values can be bound to @configurable functions and these will override the defaults:

>>> def intermediate_step():
...     gec()
...
>>> def run_application():
...     with gec.bind(threshold=11000):
...         intermediate_step()
...
>>> run_application()
Applying threshold: 11000

In this example, when the intermediate_step function calls gec, then value of the threshold that was bound to gec inside run_application, 1100, will be used instead of the default 9750. The power of this approach is that the behaviour of gec is unaffected outside of the with context:

>>> gec()
Applying threshold: 9750

Usage of bind in HLT1 and HLT2 lines is very rare, as individual lines do not usually need to configure deep down the call stack, and can instead pass changes to thresholds directly to the individual functions that create their input particles.