Skip to content

Systematics Handling

In general most CP Algorithms will have to deal with systematics, be it that they add their own systematics or that their inputs are affected by systematics, or (quite likely) both.

For that every algorithm implements an internal loop over the systematics that affect that algorithm in particular. That gets configured automatically to run the minimal set of systematics the algorithm needs to process. In general all algorithms will just have a single systematics loop, and if an algorithm would need to run multiple systematics loops it should be split into multiple algorithms.

In practical terms the algorithms can generally be written in the same way as an algorithm without systematics, just with systematics handles instead of their non-systematics equivalent. The systematics handles will determine the list of systematics to run, the exact inputs to read and the outputs to write.

Note that for efficiency purposes it is preferred to write systematics output as individual decorations instead of systematics varied containers. The issue with systematics-varied containers is that they propagate the systematics to all algorithms reading that container, while systematics-varied decorations only propagate them to algorithms reading that decoration.

A systematic is assumed to be affecting the outputs of your algorithm if it either affects one of the inputs or is introduced by the algorithm itself. This is generally the safe and conservative thing to do. However, it can also add superfluous systematics, because either you read more inputs than you needed to create a given output, or because some input systematics may technically affect the output systematics but the effect is too tiny to be relevant. An example of the later are the e-gamma momentum systematics which technically change the pt of the object, but the change is too small to be meaningful.

User Configuration Level

The CommonServices block provides options for controlling systematics evaluation. In most cases the defaults are sufficient, but the options below allow fine-tuning when needed:

CommonServices:
  runSystematics: True              # Enable/disable systematics (default: True for MC)
  filterSystematics: ".*JET.*"      # Regex to filter systematics
  onlySystematicsCategories:        # Predefined categories
    - jets
    - electrons
  systematicsHistogram: systematicsList   # Write list of evaluated systematics
  separateWeightSystematics: True         # Separate histogram for weight systematics

Available Options

runSystematics: Enable or disable computation of systematic variations. The default is None, which means systematics are run on MC but not on data. Set to False to disable all systematics processing.

filterSystematics: A regex pattern to select which systematics to evaluate. Only systematics whose names match the pattern are included. This overrides onlySystematicsCategories if both are specified.

onlySystematicsCategories: A list of predefined category names to enable. Available categories:

Category Systematics prefixes
jets JET_
jer JET_JER
electrons EG_, EL_
muons MUON_
photons EG_, PH_
taus TAUS_
met MET_
tracks TRK_
ftag FT_
event GEN_, PRW_

This is useful for partial ntuple productions or studies where you only need specific systematics.

systematicsHistogram: The name of a histogram to write containing the list of evaluated systematics. This is helpful for downstream processing frameworks that need to know which systematics were computed.

separateWeightSystematics: If systematicsHistogram is enabled, creates an additional histogram containing only weight-based systematics. This helps histogramming frameworks distinguish between systematics that require recomputing observables and those that only change weights.

Systematics in Output

Variables affected by systematics appear in the output with a %SYS% suffix that resolves to the systematic name. For example, jet pt might appear as:

  • jet_pt_NOSYS - nominal value
  • jet_pt_JET_JER_EffectiveNP_1__1up - varied by JER systematic

Variables unaffected by systematics (like eta) only appear once without the %SYS% suffix.

Configuration Block Level

When developing configuration blocks, you work with container and decoration names that include a %SYS% placeholder. The configuration system automatically manages how these map to actual systematic variations.

The %SYS% Naming Convention

Container names in the configuration automatically get %SYS% appended:

# In a config block's makeAlgs method:
alg.taus = config.readName(self.containerName)     # Returns "AnaTaus_%SYS%"
alg.tausOut = config.copyName(self.containerName)  # Creates shallow copy with new name

The %SYS% suffix is resolved at runtime to the appropriate systematic name (e.g., AnaTaus_NOSYS, AnaTaus_JET_JER_EffectiveNP_1__1up).

Container Access Methods

readName(containerName): Returns the current name of the container for reading. Use this when your algorithm only needs to read the container.

copyName(containerName): Registers that a shallow copy will be made and returns the new container name. Use this when your algorithm needs to modify objects (e.g., applying calibrations).

The order matters: If your algorithm makes a shallow copy, you must first call readName to get the existing name, before calling copyName to register the copy and update the name.

Algorithm Level

Basic Systematics Handles

The list of systematics is managed via the SysListHandle, to which all other systematics handles are connected. It also provides a list of systematics to iterate over.

For reading a container one either uses a SysReadHandle or a SysCopyHandle. The former will just read a const-qualified object, while the later will provide a mutable object (unless the template argument is const-qualified). The SysCopyHandle works internally by retrieving an object, making a shallow copy and registering it in the event store. It is typically because the underlying tool needs to operate on a mutable object (and thereby needs a shallow copy), but it can also be used if a shallow copy is needed for other reasons.

There is also a SysWriteHandle, but that is rarely used within the CP Algorithms, as most algorithms either apply corrections to existing objects or add extra decorations to them. Unlike the regular write handles if you need to write both the object and its associated aux store you do it with a single handle.

Note that unlike for Athena handles, there is no distinction between Handle and HandleKey, but instead the handles provide the functionality of both, and just return a pointer to the container directly.

If you need to read or write a decoration that is affected by systematics you need to do so via a SysReadDecorHandle or SysWriteDecorHandle. Note that these always need to be connected to the underlying container handle, and don't allow retrieving the underlying container (unlike the Athena decoration handles).

Important: You have to initialize all the systematics handles inside initialize.

Notes from the Developers

The basic idea of the systematics handling is that if you have an algorithm that works and doesn't have systematics handling, adding systematics support should be mostly a straightforward process of replacing existing interactions with the event store and adding a systematics list.

The systematics handle interface is based on the AsgTool::evtStore() interface, not the AthenaMT data handles. In part that is because that was the dominant interface in analysis at the time, but the AthenaMT data handles are also far more complex to implement, and they also provide a lot of extra behavior and guarantees that are likely to be more hurtful than helpful in our use case.

While it is discouraged, it is possible to use the StoreGate data handles in the CP Algorithms, as long as they are run upstream of any systematics. Even if your algorithm doesn't introduce any systematics, if the object has systematics you have to switch to systematics handles.

The systematics handles anticipate to be used in AthenaMT eventually, but they don't support it yet. To add support for AthenaMT one would have to either layer the systematics handles on StoreGate data handles (preferred), or have to directly declare the data dependencies they introduce. This ought to be fairly straightforward, but the assumption is that the CP tools themselves likely still contain non thread-safe constructs internally.

The motivation for the copy handles is two-fold: For analysis it is quite common to update objects, and it is quite tedious to do a manual shallow copy each time. So something like an Athena UpdateHandle would be ideal, and the copy handle is a multi-threading safe analogue. Also, for adding momentum systematics it is necessary to do a shallow copy per systematic (so that they each get their own pt in the aux-store). That is much more practical if the handles do the copy. This was even more important in the past when all systematics where implemented via shallow copies.

Originally systematics were handled purely through shallow copies, including shallow copies for scale factor systematics, etc. While that worked technically the problem is that it means that systematics are tracked at the level of containers instead of decorations. That is far too coarse, e.g. electron pt would get any systematic that affects any systematic that affects any electron decoration, which if you apply overlap removal also includes things like the jet momentum systematics. So we switched most systematics to be differently named decorations on the same container. For some variables (particularly pt, etc.) that is not possible, since their name is hard-coded in the xAOD core classes, otherwise we'd likely switch completely to that mechanism.