Source code for columnflow.calibration.cms.met

# coding: utf-8

"""
MET corrections.
"""

import law

from columnflow.calibration import Calibrator, calibrator
from columnflow.util import maybe_import, load_correction_set, DotDict
from columnflow.columnar_util import set_ak_column
from columnflow.types import Any

np = maybe_import("numpy")
ak = maybe_import("awkward")


[docs] @calibrator( uses={"run", "PV.npvs"}, # name of the MET collection to calibrate met_name="MET", # function to determine the correction file get_met_file=(lambda self, external_files: external_files.met_phi_corr), # function to determine met correction config get_met_config=(lambda self: self.config_inst.x.met_phi_correction_set), ) def met_phi(self: Calibrator, events: ak.Array, **kwargs) -> ak.Array: """ Performs the MET phi (type II) correction using the :external+correctionlib:doc:`index` for events there the uncorrected MET pt is below the beam energy (extracted from ``config_inst.campaign.ecm * 0.5``). Requires an external file in the config under ``met_phi_corr``: .. code-block:: python cfg.x.external_files = DotDict.wrap({ "met_phi_corr": "/afs/cern.ch/work/m/mrieger/public/mirrors/jsonpog-integration-9ea86c4c/POG/JME/2017_UL/met.json.gz", # noqa }) *get_met_file* can be adapted in a subclass in case it is stored differently in the external files. The name of the correction set should be present as an auxiliary entry in the config: .. code-block:: python cfg.x.met_phi_correction_set = "{variable}_metphicorr_pfmet_{data_source}" where "variable" and "data_source" are placeholders that are inserted in the calibrator setup :py:meth:`~.met_phi.setup_func`. *get_met_correction_set* can be adapted in a subclass in case it is stored differently in the config. :param events: awkward array containing events to process """ # get Met columns met = events[self.met_name] # copy the intial pt and phi values corr_pt = np.array(met.pt, dtype=np.float32) corr_phi = np.array(met.phi, dtype=np.float32) # select only events where MET pt is below the expected beam energy mask = met.pt < (0.5 * self.config_inst.campaign.ecm) # arguments for evaluation args = ( met.pt[mask], met.phi[mask], ak.values_astype(events.PV.npvs[mask], np.float32), ak.values_astype(events.run[mask], np.float32), ) # evaluate and insert corr_pt[mask] = self.met_pt_corrector.evaluate(*args) corr_phi[mask] = self.met_phi_corrector.evaluate(*args) # save the corrected values events = set_ak_column(events, f"{self.met_name}.pt", corr_pt, value_type=np.float32) events = set_ak_column(events, f"{self.met_name}.phi", corr_phi, value_type=np.float32) return events
@met_phi.init def met_phi_init(self: Calibrator, **kwargs) -> None: """ Initialize the :py:attr:`met_pt_corrector` and :py:attr:`met_phi_corrector` attributes. """ self.uses.add(f"{self.met_name}.{{pt,phi}}") self.produces.add(f"{self.met_name}.{{pt,phi}}") @met_phi.requires def met_phi_requires( self: Calibrator, task: law.Task, reqs: dict[str, DotDict[str, Any]], **kwargs, ) -> None: if "external_files" in reqs: return from columnflow.tasks.external import BundleExternalFiles reqs["external_files"] = BundleExternalFiles.req(task) @met_phi.setup def met_phi_setup( self: Calibrator, task: law.Task, reqs: dict[str, DotDict[str, Any]], inputs: dict[str, Any], reader_targets: law.util.InsertableDict, **kwargs, ) -> None: """ Load the correct met files using the :py:func:`from_string` method of the :external+correctionlib:py:class:`correctionlib.highlevel.CorrectionSet` function and apply the corrections as needed. :param reqs: Requirement dictionary for this :py:class:`~columnflow.calibration.Calibrator` instance :param inputs: Additional inputs, currently not used. :param reader_targets: Additional targets, currently not used. """ # create the pt and phi correctors met_file = self.get_met_file(reqs["external_files"].files) correction_set = load_correction_set(met_file) name_tmpl = self.get_met_config() self.met_pt_corrector = correction_set[name_tmpl.format( variable="pt", data_source=self.dataset_inst.data_source, )] self.met_phi_corrector = correction_set[name_tmpl.format( variable="phi", data_source=self.dataset_inst.data_source, )] # check versions if self.met_pt_corrector.version not in (1,): raise Exception(f"unsuppprted met pt corrector version {self.met_pt_corrector.version}") if self.met_phi_corrector.version not in (1,): raise Exception(f"unsuppprted met phi corrector version {self.met_phi_corrector.version}")