Skip to content

Config Block Factories

To allow users to create your blocks, you need to register them with the factory system. This enables the YAML configuration parser to find and instantiate your blocks when users reference them by name.

Central Registration (athena repository)

For blocks defined within the athena repository, registration is done centrally in ConfigFactory.py within the addDefaultAlgs method.

To register a new block, add an import and registration call to addDefaultAlgs:

def addDefaultAlgs(self):
    # ... existing registrations ...

    # my new block
    from MyPackage.MyConfig import MyCalibrationConfig
    self.addAlgConfigBlock(algName="MyCalibration", alg=MyCalibrationConfig)

You can also provide default values that override the defaults defined in the block class:

    self.addAlgConfigBlock(algName="MyBlock", alg=MyBlockConfig,
        defaults={'selectionName': ''})

Block Ordering

The order in which blocks are registered in addDefaultAlgs defines their default execution order. When block A depends on block B (e.g. MET needing fully corrected good objects), block A should be registered after block B in the factory.

While there are more advanced mechanisms for controlling block ordering, this basic approach of placing dependent blocks after their dependencies in the registration order is usually sufficient.

Sub-blocks

For sub-blocks that should appear nested under a parent block, use the superBlocks parameter:

    from MyPackage.MyConfig import MyWorkingPointConfig
    self.addAlgConfigBlock(algName="WorkingPoint", alg=MyWorkingPointConfig,
        superBlocks="MyCalibration")

This creates a hierarchical structure where the sub-block appears nested under its parent in YAML. Users then configure it as:

Jets:
  containerName: AnaJets
  JVT:
    workingPoint: "Tight"

This organizational pattern keeps related options grouped together in the configuration.

Sub-blocks should be registered after their parent block, so that they can rely on containers and decorations it created. In this example, the JVT block should come after the Jets block. Similarly, working point blocks for electrons and muons come after their respective calibration blocks.

Class-Based vs Function-Based Registration

Class-based registration (preferred)

When you register a ConfigBlock class directly, the factory instantiates it and sets options via setOptionValue():

factory.addAlgConfigBlock(algName="MyMuons", alg=MyMuonCalibrationConfig)

This is the preferred approach because all options are declared on the block itself and the system can automatically discover them for documentation and validation.

Function-based registration

You can also register a factory function that receives a seq parameter (the ConfigSequence) and creates blocks programmatically:

def makeMyConfig(seq, containerName, someOption=True):
    config = MyConfigBlock()
    config.setOptionValue('containerName', containerName)
    config.setOptionValue('someOption', someOption)
    seq.append(config)

factory.addAlgConfigBlock(algName="MyBlocks", alg=makeMyConfig)

The function's parameters (other than seq) become the user-facing options. Use this approach sparingly; prefer declaring options on the block itself rather than as function parameters.

Factories Producing Multiple Blocks

A factory function can add multiple blocks to the sequence, which is useful when you need prerequisite blocks or want to bundle related functionality:

def makeMultiBlockConfig(seq, containerName):
    # Add prerequisite block
    prereqBlock = PrerequisiteConfig()
    prereqBlock.setOptionValue('containerName', containerName)
    seq.append(prereqBlock)

    # Add main block
    mainBlock = MainConfig()
    mainBlock.setOptionValue('containerName', containerName)
    seq.append(mainBlock)

Registering Blocks via YAML (user-defined blocks)

For blocks defined outside the athena repository, the recommended approach is to register them dynamically using AddConfigBlocks in your YAML configuration. The preferred format uses a map with the block name as the key:

AddConfigBlocks:
  MyMuons:
    modulePath: "MyPackage.MyConfig"
    functionName: "MyMuonCalibrationConfig"

This tells the system to import MyMuonCalibrationConfig from MyPackage.MyConfig and register it under the name MyMuons. Users can then use it in their configuration:

MyMuons:
  containerName: AnalysisMuonsCalib
  extraVariables: true

The AddConfigBlocks entry supports additional optional fields:

AddConfigBlocks:
  MyMuons:
    modulePath: "MyPackage.MyConfig"
    functionName: "MyMuonCalibrationConfig"
    defaults:
      inputContainer: "CustomMuons"  # Override default option values
    superBlocks: "Muons"             # Register as sub-block of Muons

Alternatively AddConfigBlocks also supports a list format, but it is deprecated because it doesn't work well with YAML file merging:

# List format (not recommended)
AddConfigBlocks:
  - modulePath: "MyPackage.MyConfig"
    functionName: "MyMuonCalibrationConfig"
    algName: "MyMuons"