diff --git a/src/coffea/nanoevents/methods/nanoaod.py b/src/coffea/nanoevents/methods/nanoaod.py index 1681e4a54..51b3e773d 100644 --- a/src/coffea/nanoevents/methods/nanoaod.py +++ b/src/coffea/nanoevents/methods/nanoaod.py @@ -13,7 +13,9 @@ class _NanoAODEvents(behavior["NanoEvents"]): def __repr__(self): - return f"" + return f"" behavior["NanoEvents"] = _NanoAODEvents diff --git a/src/coffea/nanoevents/schemas/nanoaod.py b/src/coffea/nanoevents/schemas/nanoaod.py index ac64f3380..e119d698c 100644 --- a/src/coffea/nanoevents/schemas/nanoaod.py +++ b/src/coffea/nanoevents/schemas/nanoaod.py @@ -37,10 +37,18 @@ class NanoAODSchema(BaseSchema): There is a class-level variable ``warn_missing_crossrefs`` which will alter the behavior of NanoAODSchema. If warn_missing_crossrefs is true then when a missing global index cross-ref target is encountered a warning will be issued. Regardless, the cross-reference is dropped. + + The same holds for ``error_missing_events_id``. If error_missing_events_id is true, then when the 'run', 'event', + or 'luminosityBlock' fields are missing, an exception will be thrown; if it is false, just a warning will be issued. """ __dask_capable__ = True warn_missing_crossrefs = True + error_missing_event_ids = True + + event_ids = ["run", "luminosityBlock", "event"] + """List of NanoAOD event IDs + """ mixins = { "CaloMET": "MissingET", @@ -206,6 +214,26 @@ def _build_collections(self, field_names, input_contents): branch_forms["n" + name] ) + # Check the presence of the event_ids + missing_event_ids = [ + event_id for event_id in self.event_ids if event_id not in branch_forms + ] + if len(missing_event_ids) > 0: + if self.error_missing_event_ids: + raise RuntimeError( + f"There are missing event ID fields: {missing_event_ids} \n\n\ + The event ID fields {self.event_ids} are necessary to perform sub-run identification \ + (e.g. for corrections and sub-dividing data during different detector conditions),\ + to cross-validate MC and Data (i.e. matching events for comparison), and to generate event displays. \ + It's advised to never drop these branches from the dataformat.\n\n\ + This error can be demoted to a warning by setting the class level variable error_missing_event_ids to False." + ) + else: + warnings.warn( + f"Missing event_ids : {missing_event_ids}", + RuntimeWarning, + ) + # Create global index virtual arrays for indirection for indexer, target in self.cross_references.items(): if target.startswith("Gen") and isData: diff --git a/tests/samples/missing_luminosityBlock.root b/tests/samples/missing_luminosityBlock.root new file mode 100644 index 000000000..644a0253a Binary files /dev/null and b/tests/samples/missing_luminosityBlock.root differ diff --git a/tests/test_nanoevents.py b/tests/test_nanoevents.py index cd4b7e439..e1fd0e92d 100644 --- a/tests/test_nanoevents.py +++ b/tests/test_nanoevents.py @@ -156,3 +156,20 @@ def test_read_nanodata(suffix): crossref(events) crossref(events[ak.num(events.Jet) > 2]) + + +def test_missing_eventIds_error(): + path = os.path.abspath("tests/samples/missing_luminosityBlock.root") + ":Events" + with pytest.raises(RuntimeError): + factory = NanoEventsFactory.from_root(path, schemaclass=NanoAODSchema) + factory.events() + + +def test_missing_eventIds_warning(): + path = os.path.abspath("tests/samples/missing_luminosityBlock.root") + ":Events" + with pytest.warns( + RuntimeWarning, match=r"Missing event_ids \: \[\'luminosityBlock\'\]" + ): + NanoAODSchema.error_missing_event_ids = False + factory = NanoEventsFactory.from_root(path, schemaclass=NanoAODSchema) + factory.events()