Skip to content

Configuration system

FLAF's behaviour — which datasets exist, which corrections apply, where outputs go, which processes make up the analysis — is driven by YAML configuration. This page explains how the configuration is assembled. For how to change specific things, see the Configuration guide.

The implementation lives in FLAF/Common/Setup.py.

Four layers, merged in order

When you run a task with --period <era>, FLAF loads configuration from four directories, in this order:

config_path_order = [
    "<analysis>/FLAF/config",          # 1. framework defaults (all analyses)
    "<analysis>/FLAF/config/<era>",    # 2. framework defaults for this era
    "<analysis>/config",               # 3. analysis-wide settings
    "<analysis>/config/<era>",         # 4. analysis settings for this era  ← wins
]

Think of it as general → specific: the framework provides sensible cross-analysis defaults (layer 1–2), and each analysis overrides or extends them (layer 3–4). The per-era directories let 2022 and 2023 differ without duplicating everything.

How values combine

The merge rule depends on the value type, and this distinction matters:

  • Scalars (a string, a number, a bool): a later layer overrides an earlier one. So analysis/config/<era> has the final say.
  • Lists (most importantly datasets.yaml): later layers extend (concatenate) earlier ones. Nothing is lost.

The list behaviour is why datasets are split across files instead of duplicated: SM backgrounds and data live in the framework's FLAF/config/<era>/datasets.yaml, while signals and custom samples live in the analysis's config/<era>/datasets.yaml. After merging, all of them are available together. See Datasets.

The objects you may meet in code

Class (in Common/Setup.py) Role
Config Loads and merges the YAML files from the four directories for one logical config (e.g. "datasets"). Accessed like a dict: cfg["key"], cfg.get("key", default).
Setup The master configuration object. Built once per (analysis, period); holds the merged datasets, processes, the physics model and more.
PhysicsModel Classifies each process as background, signal or data, and expands meta-processes (parameterised families, e.g. all resonant masses).

Setup is a singleton: anywhere in the code, Setup.getGlobal() returns the one instance for the current run, so all tasks see a consistent configuration.

The key configuration files

File Lives in Holds
user_custom.yaml analysis config/ Your personal, uncommitted settings: storage, model, options. Guide.
global.yaml both layers Global settings: anaTuple/histTuple definitions, corrections, payload producers, signal types.
datasets.yaml per-era, both layers Dataset (sample) definitions. Guide.
processes.yaml analysis config/ Logical processes built from datasets. Guide.
phys_models.yaml analysis config/ Which processes are background/signal/data for a model. Guide.
crossSections13p6TeV.yaml FLAF/config/ Cross-section values referenced by datasets.

Validate your config without running the pipeline

Loading Setup.py for an era is exactly what the test-setup-loading CI check does — it catches typos and missing references early. You can do the same locally by constructing the setup for an era; if it loads, the config is internally consistent.

user_custom.yaml is part of the merge too

Your config/user_custom.yaml overlays the merged configuration with personal values (storage locations, phys_model, options like compute_unc_variations). For a single run you can layer an extra file on top with --user-custom <path>, which is loaded last and therefore wins — handy for one-off tests without editing your committed file. See user_custom.yaml.