23.6. Configuration Parameters
Table of Contents
Why Configuration Parameters Matter
Configuration parameters separate the logic of your analysis from the values that may change between runs. These values include cuts, file paths, histogram binning, and switches to enable or disable parts of the analysis. By externalizing such values, you avoid editing and recompiling core code every time a condition changes, and you reduce the chance of mistakes when reusing scripts for different datasets or systematic variations.
In ROOT-based analyses, configuration parameters are a cornerstone for flexibility, reproducibility, and collaboration. When several people work on the same analysis, having a clear and centralized configuration makes the code easier to understand and modify, because the physics choices are visible in one place rather than scattered across many macros.
A ROOT analysis that uses hard-coded numbers everywhere is fragile. Centralize all changeable numbers and options in a configuration so that you can rerun the same code with different settings without editing the source.
What Should Become a Configuration Parameter
Configuration parameters are values that you expect might change across runs, samples, or analysis stages. They are not every number in your code, but all numbers and strings that qualify as analysis choices rather than implementation details.
Common examples include input and output locations, numerical selections, and switches that control which plots or modules run. Parameters that influence physics results, such as selection thresholds, are particularly important to externalize, because they must be clearly documented and often systematically varied.
At the same time, you do not want to over-parameterize trivial implementation constants that will never change. For instance, an internal scaling factor used to normalize debug prints is not as important as the integrated luminosity used to scale histograms to cross sections.
A reasonable guideline is: if you can imagine a collaborator asking to change a value without rewriting the algorithm, then that value is a good candidate for a configuration parameter.
Centralizing Configuration in Code
Even if you do not use external configuration files yet, you can greatly improve your ROOT macros by collecting all configuration settings into one place inside the C++ code. This can be a simple namespace, a struct, or a small configuration class that lives at the top of your main analysis file.
A basic approach uses a struct that groups related settings together. For example, you can have one group for files, one for cuts, and one for plotting options. This makes it explicit which numbers control which part of the analysis, and it lets you pass a single configuration object into your functions instead of long argument lists.
You should also avoid using global variables spread over many macros for configuration. A single configuration object that is passed around or accessed in a controlled way is easier to manage. It also helps when you want to run several configurations in the same program, for example scanning different cut values, because you can construct several configuration objects instead of touching global state.
Keep configuration values in a single, centralized location. Do not scatter literals like 3.14, 0.7, 120.0, or "data.root" throughout your analysis code. Use named configuration fields instead.
Configuring Paths, Names, and File Handling
One of the first things to parameterize in a ROOT analysis is the set of paths and file names your code uses. These include input ROOT files, lists of files, output directories, and standardized naming conventions for histograms, graphs, and log files.
Hard-coding absolute paths such as /home/user/data/... inside a macro ties the analysis to a particular machine and user. Instead, use configuration parameters for a base directory and relative paths to datasets. This makes it easy to move the entire analysis to a different location or environment, because you only have to adjust the configuration and not the implementation code.
You should also parameterize output locations. A common pattern is to have a top-level output directory and subdirectories for plots, intermediate ROOT files, and logs. By keeping these paths in configuration, you can separate different runs by date or by configuration tag without editing the logic.
A useful practice is to include a short configuration name or tag in output file names. This allows you to later identify which set of parameters produced each file and avoid overwriting results from different test runs when you experiment with cuts or models.
Configuring Selections and Cuts
Selection cuts are central to most ROOT analyses. They define which events or objects are considered signal-like or background-like. Because they encode physics choices, they must be easy to read, documented, and changeable. For that reason they are prime candidates to live in configuration.
Typical examples are minimum and maximum values for transverse momentum, energy, multiplicities, time windows, angular regions, and quality criteria. If you define these numbers directly inside loops or if statements, every change requires code modification and increases the risk that some part of the code still uses an old value.
Instead, define descriptive configuration parameters such as minEnergy, maxEta, or minHits and use these in your selection code. This improves readability and reduces "magic numbers" in your source. When you later want to perform cut variations or systematic studies, you can produce several configurations, each with different selection values, and run the same code with each one.
In more advanced analyses, it is also helpful to configure entire sets of selection definitions by name. For example, you can define "loose", "medium", and "tight" selection levels in the configuration and choose among them at runtime. The underlying numeric thresholds then stay encapsulated, and the code operates at a more semantic level.
Never bury selection thresholds directly in if conditions. Always define them as named configuration parameters. This is essential for clear documentation and for consistent cut variations.
Configuring Histogram and Plot Settings
Histogram definitions are another major consumer of configuration parameters. The number of bins, minimum and maximum of each axis, and even titles and axis labels are parameters that often need to be tuned. When these values are embedded directly in calls such as TH1F("h", "title", 100, 0, 200) they are difficult to change across a large code base.
Instead, store binning information and labels in a configuration structure. For each observable you might have entries such as the number of bins, lower and upper range, X-axis title, and Y-axis title. Your code for creating histograms then reads these values from the configuration instead of hard-coding them. This approach also prepares you for situations where the binning needs to change depending on the dataset or detector conditions.
Plot styling can be treated similarly. Marker types, line colors, legend positions, and canvas sizes can all be provided by a configuration layer. While some styling preferences may be global for the whole analysis, others may need to vary by plot or by output context, for example screen review versus publication. Parameterizing these settings lets you adapt the look of your plots without editing many lines of drawing code.
At a higher level, you can also configure which plots should be produced in a given run. A simple flag per plot, defined in the configuration, can tell your analysis whether to fill and draw that histogram. This avoids having to comment or uncomment plotting code when you only want a subset of all possible figures.
Configuration Files vs Hard-Coded Settings
Centralizing configuration inside C++ code is already an improvement over scattered literals, but it still requires recompilation or at least code editing when values change. To gain more flexibility, you can move configuration data into external files that your ROOT analysis reads at runtime.
Text-based configuration files allow you to adjust parameters without touching the source. They also let you keep several configurations side by side, for instance one for each dataset or physics scenario, and choose the desired one through a command-line argument or an environment variable.
There are several simple file formats that are easy to parse from C++ and ROOT, such as plain key-value .cfg files, JSON, or YAML. For beginner-friendly ROOT code, you can start with a simple format where each line contains a parameter name and a value, ignoring comment lines. As your analysis grows, you can adopt more structured formats that support nested sections and arrays.
When using external configuration files, it is a good habit to validate the values after reading them. For example, check that bin counts are positive, that minimums are less than maximums, and that paths exist when required. If there is a problem with the configuration, fail early with a clear error message rather than letting ROOT crash in the middle of an event loop.
Move frequently changed values to external configuration files. This lets you rerun the same compiled analysis with different cuts, file lists, and plotting options, and it reduces the risk of editing mistakes in the analysis source code.
Passing Configuration Through Your Analysis
Once you have a central configuration, either in code or in an external file, you need a clean way to make it available to different parts of your analysis. The simplest approach is to create a configuration object in your main function or main macro and pass a reference to it down to the functions that need it.
This approach avoids the use of global variables and makes each function more explicit about the configuration it consumes. When you examine a function signature, you immediately see that it relies on external settings. It also simplifies unit testing, because you can construct small configuration objects specific to each test.
In some cases, you may wrap the configuration object in a shared pointer or keep it as a member of an analysis class that encapsulates the whole workflow. This fits particularly well with more complex ROOT analyses, where you might have different modules for reading TTrees, applying selections, and producing plots.
Be careful not to mix configuration values with computed results inside the same structure. Configuration should represent input choices, not analysis outputs. Keeping this separation clear prevents subtle bugs where code accidentally modifies configuration during processing.
Documenting and Versioning Configuration
A configuration is part of your analysis logic, so it deserves documentation and version control just like the C++ code. Every parameter should have a clear meaning and, when relevant, physical units. Descriptive naming helps, but it is often useful to include comments either in the configuration file or in a separate README that explains the choice of values.
Storing configuration files in the same version control repository as the ROOT code ensures that you can reconstruct exactly which parameters were used for a particular result. When you change a configuration, the version control history records the differences in a readable way. This is essential for reproducible analyses and for explaining changes in results to collaborators or reviewers.
When you prepare final results, it is also helpful to write the used configuration into the output directory, for example as a copy of the configuration file or as text stored inside a ROOT file. This allows anyone with the output to inspect the exact parameters without needing access to the original repository.
Treat configurations as first-class analysis artifacts. Keep them under version control, document them, and archive the exact configuration used for each set of final results.
Configuration for Systematic Variations and Runs
Configuration parameters are especially powerful when you perform systematic studies or explore different analysis strategies. For example, you might want to vary cut thresholds, change histogram binning, or switch between different calibration constants. Rather than editing the code for each variation, you can create multiple configuration files or multiple configuration objects that reflect different scenarios.
You can define a "base" configuration and then produce modified versions for specific variations. For instance, you might scale a cut by a factor, swap input datasets, or enable additional histograms. Your main program can loop over a set of configurations and run the same analysis logic for each one, producing distinct outputs for later comparison.
It is also useful to include a label or tag within each configuration that is automatically embedded into output file names and plots. This prevents confusion when you compare results from different runs and ensures that the provenance of each dataset is clear.
By designing your ROOT analysis with configuration parameters in mind, you make it much easier to explore the parameter space of your study, perform robustness checks, and respond quickly to new requirements or feedback, all without rewriting or duplicating your core analysis code.
Views: 13
KAHIBARO