diff --git a/docs/core.rst b/docs/core.rst index 5590b078..101308f8 100644 --- a/docs/core.rst +++ b/docs/core.rst @@ -116,13 +116,12 @@ SamplePath The ``SamplePath`` setting sets the value for the ``{SamplePath}`` placeholder. In contrast to the ntuple case, this value cannot be a list of strings. -It also cannot be overridden on a per-systematic basis, just like ``RegionPath``. VariationPath """"""""""""" Each systematic template can set the value for the ``{VariationPath}`` placeholder via the ``VariationPath`` setting. -``RegionPath`` and ``SamplePath`` settings cannot be overridden. +The ``RegionPath`` setting cannot be overridden. An example """""""""" diff --git a/src/cabinetry/contrib/histogram_creator.py b/src/cabinetry/contrib/histogram_creator.py index 60151072..ebe5e350 100644 --- a/src/cabinetry/contrib/histogram_creator.py +++ b/src/cabinetry/contrib/histogram_creator.py @@ -1,5 +1,6 @@ """Creates histograms from ntuples with uproot.""" +import logging import pathlib from typing import List, Optional @@ -8,6 +9,9 @@ import uproot +log = logging.getLogger(__name__) + + def with_uproot( ntuple_paths: List[pathlib.Path], pos_in_file: str, @@ -51,6 +55,8 @@ def with_uproot( weight_is_expression = False weight = "1.0" + log.debug(f"reading input ntuples {paths_with_trees}") + if weight_is_expression: # need to read observables and weights array_generator = uproot.iterate( diff --git a/src/cabinetry/contrib/histogram_reader.py b/src/cabinetry/contrib/histogram_reader.py index aaf4fab0..e7487069 100644 --- a/src/cabinetry/contrib/histogram_reader.py +++ b/src/cabinetry/contrib/histogram_reader.py @@ -1,9 +1,14 @@ """Reads histograms with uproot.""" +import logging + import boost_histogram as bh import uproot +log = logging.getLogger(__name__) + + def with_uproot(histo_path: str) -> bh.Histogram: """Reads a histogram with uproot and returns it. @@ -14,5 +19,6 @@ def with_uproot(histo_path: str) -> bh.Histogram: Returns: bh.Histogram: histogram containing data """ + log.debug(f"reading input histogram {histo_path}") hist = uproot.open(histo_path).to_boost() return hist diff --git a/src/cabinetry/templates/builder.py b/src/cabinetry/templates/builder.py index 13040cf1..5c7ea89b 100644 --- a/src/cabinetry/templates/builder.py +++ b/src/cabinetry/templates/builder.py @@ -29,9 +29,9 @@ def _ntuple_paths( A path is built starting from the path specified in the general options in the configuration file. This path can contain placeholders for region- and sample- specific overrides, via ``{Region}`` and ``{Sample}``. For non-nominal templates, it - is possible to override the sample path if the ``SamplePath`` option is specified - for the template. If ``SamplePath`` is a list, return a list of paths (one per - entry in the list). + is possible to override the path if the ``RegionPath`` or ``SamplePath`` options are + specified for the template. If ``SamplePath`` is a list, return a list of paths + (one per entry in the list). Args: general_path (str): path specified in general settings, with sections that can diff --git a/src/cabinetry/templates/collector.py b/src/cabinetry/templates/collector.py index c20df2cc..f8135bc3 100644 --- a/src/cabinetry/templates/collector.py +++ b/src/cabinetry/templates/collector.py @@ -26,7 +26,9 @@ def _histo_path( location of the histogram within the file. Due to the presence of this colon, the return value is a string instead of a pathlib path. A path is built starting from the path specified in the general options in the configuration file. This path - contains placeholders for region-, sample-, and systematic-specific values. + contains placeholders for region-, sample-, and systematic-specific values. For + non-nominal templates there are overrides for ``SamplePath`` and ``VariationPath`` + which can be used. Args: general_path (str): path specified in general settings, with sections that can @@ -53,7 +55,12 @@ def _histo_path( # check whether a systematic is being processed if template is not None: - # determine whether the template has an override for VariationPath specified + # determine whether the template has an override for SamplePath specified + sample_override = utils._check_for_override(systematic, template, "SamplePath") + if sample_override is not None: + sample_path = sample_override + + # check for VariationPath override variation_override = utils._check_for_override( systematic, template, "VariationPath" ) @@ -61,16 +68,12 @@ def _histo_path( # _check_for_override should return Optional[str] for VariationPath, but # mypy cannot know that it will not be a list, so explicitly cast to str variation_path = cast(str, variation_override) - else: - log.warning( - f"no VariationPath override specified for {region['Name']} / " - f"{sample['Name']} / {systematic['Name']} {template}" - ) - # apply variation-specific setting - path = general_path.replace("{VariationPath}", variation_path) + + # create a new string for path handling, with placeholders replaced subsequently + path = general_path # handle region-specific setting - region_template_exists = "{RegionPath}" in general_path + region_template_exists = "{RegionPath}" in path if region_path is not None: if not region_template_exists: log.warning( @@ -81,7 +84,7 @@ def _histo_path( raise ValueError(f"no path setting found for region {region['Name']}") # handle sample-specific setting - sample_template_exists = "{SamplePath}" in general_path + sample_template_exists = "{SamplePath}" in path if sample_path is not None: if not sample_template_exists: log.warning( @@ -91,6 +94,15 @@ def _histo_path( elif sample_template_exists: raise ValueError(f"no path setting found for sample {sample['Name']}") + # handle variation-specific setting (variation_path is always specified via function + # argument and possibly also via override) + if "{VariationPath}" not in path: + log.warning( + "variation override specified, but {VariationPath} not found in default " + "path" + ) + path = path.replace("{VariationPath}", variation_path) + # check for presence of colon to distinguish path to file and location within file if ":" not in path: log.warning(f"no colon found in path {path}, may not be able to find histogram") diff --git a/tests/templates/test_templates_collector.py b/tests/templates/test_templates_collector.py index 432efcab..bcf305f0 100644 --- a/tests/templates/test_templates_collector.py +++ b/tests/templates/test_templates_collector.py @@ -13,7 +13,7 @@ def test__histo_path(caplog): # only general path, no override assert collector._histo_path("path.root:h1", "", {}, {}, {}, None) == "path.root:h1" - # general path with region, sample and nominal variation + # general path with region, sample and nominal variation, template with no impact assert ( collector._histo_path( "{RegionPath}.root:{SamplePath}_{VariationPath}", @@ -21,41 +21,23 @@ def test__histo_path(caplog): {"RegionPath": "region"}, {"SamplePath": "sample"}, {}, - None, + "Up", ) == "region.root:sample_nominal" ) - # systematic with override for VariationPath + # systematic with override for SamplePath and VariationPath assert ( collector._histo_path( "{RegionPath}.root:{SamplePath}_{VariationPath}", "nominal", {"RegionPath": "reg_1"}, {"SamplePath": "path"}, - {"Name": "variation", "Up": {"VariationPath": "up"}}, - "Up", - ) - == "reg_1.root:path_up" - ) - - caplog.clear() - - # systematic without override, results in warning from VariationPath - assert ( - collector._histo_path( - "f.root:h1_{VariationPath}", - "", - {"Name": "reg"}, - {"Name": "sam"}, - {"Name": "variation"}, + {"Name": "variation", "Up": {"SamplePath": "var", "VariationPath": "up"}}, "Up", ) - == "f.root:h1_" + == "reg_1.root:var_up" ) - assert "no VariationPath override specified for reg / sam / variation Up" in [ - rec.message for rec in caplog.records - ] caplog.clear() # warning: no region path in template @@ -82,6 +64,19 @@ def test__histo_path(caplog): ] caplog.clear() + # warning: no variation path in template + assert ( + collector._histo_path( + "f.root:h1", "", {}, {"VariationPath": "sample.root"}, {}, None + ) + == "f.root:h1" + ) + assert ( + "variation override specified, but {VariationPath} not found in default path" + in [rec.message for rec in caplog.records] + ) + caplog.clear() + # warning: no colon in path assert collector._histo_path("f.root", "", {}, {}, {}, None) == "f.root" assert "no colon found in path f.root, may not be able to find histogram" in [