diff --git a/higgs_dna/metaconditions/Era2017_legacy_v1.json b/higgs_dna/metaconditions/Era2017_legacy_v1.json index 2b4a3d72907a8a61a58e0e1b6795648ff65fa62d..e24ef337e42acdc3091e9f8de73a59990f617bfe 100644 --- a/higgs_dna/metaconditions/Era2017_legacy_v1.json +++ b/higgs_dna/metaconditions/Era2017_legacy_v1.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:253243b86ce5aa3a6229e7b5a0fb97281f4d92adbedee2bf4174a0ad0a978c95 -size 17606 +oid sha256:d94b99c1a5b7ae1e404c935931d100fbc72a56c944a850de8b478374cab76355 +size 17757 diff --git a/higgs_dna/metaconditions/Era2022_v1.json b/higgs_dna/metaconditions/Era2022_v1.json index d8dbee1f86ce259e934cd6f517409a254a371c40..b6d688d50633fb74d98b8ab37169fbeba7d54d34 100755 --- a/higgs_dna/metaconditions/Era2022_v1.json +++ b/higgs_dna/metaconditions/Era2022_v1.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b40b09b78f538b159bdc85ac29c359108bf96dc5990345d7d361819d401dd619 -size 17127 +oid sha256:5a67cb173e47b7f234c1a47e2819ebc0358f8bf8fc4b635e325ecb1825fe0b27 +size 17237 diff --git a/higgs_dna/selections/photon_selections.py b/higgs_dna/selections/photon_selections.py index c1a3a2f833a13e1cdcc27651fec3fad8a6d754e7..be148e6ecb9048aa1778fc4ad4a773d9369c2cc6 100644 --- a/higgs_dna/selections/photon_selections.py +++ b/higgs_dna/selections/photon_selections.py @@ -11,6 +11,7 @@ def photon_preselection( events: awkward.Array, apply_electron_veto=True, year="2023", + IsFlag=False ) -> awkward.Array: """ Apply preselection cuts to photons. @@ -141,18 +142,37 @@ def photon_preselection( ) # not apply electron veto for for TnP workflow e_veto = self.e_veto if apply_electron_veto else -1 - return photons[ - (photons.electronVeto > e_veto) - & (photons.pt > self.min_pt_photon) - & (photons.isScEtaEB | photons.isScEtaEE) - & (photons.mvaID > self.min_mvaid) - & (photons.hoe < self.max_hovere) - & ( - (photons.r9 > self.min_full5x5_r9) - | ( - rel_iso * photons.pt < self.max_chad_iso + + if IsFlag: + photons["PassPresel"] = ( + (photons.electronVeto > e_veto) + & (photons.pt > self.min_pt_photon) + & (photons.isScEtaEB | photons.isScEtaEE) + & (photons.mvaID > self.min_mvaid) + & (photons.hoe < self.max_hovere) + & ( + (photons.r9 > self.min_full5x5_r9) + | ( + rel_iso * photons.pt < self.max_chad_iso + ) + | (rel_iso < self.max_chad_rel_iso) ) - | (rel_iso < self.max_chad_rel_iso) + & (isEB_high_r9 | isEB_low_r9 | isEE_high_r9 | isEE_low_r9) ) - & (isEB_high_r9 | isEB_low_r9 | isEE_high_r9 | isEE_low_r9) - ] + return photons + else: + return photons[ + (photons.electronVeto > e_veto) + & (photons.pt > self.min_pt_photon) + & (photons.isScEtaEB | photons.isScEtaEE) + & (photons.mvaID > self.min_mvaid) + & (photons.hoe < self.max_hovere) + & ( + (photons.r9 > self.min_full5x5_r9) + | ( + rel_iso * photons.pt < self.max_chad_iso + ) + | (rel_iso < self.max_chad_rel_iso) + ) + & (isEB_high_r9 | isEB_low_r9 | isEE_high_r9 | isEE_low_r9) + ] diff --git a/higgs_dna/workflows/__init__.py b/higgs_dna/workflows/__init__.py index 86db257c5b56f6c7615cda4378f6f435d0e18207..11aa4aa0ae72f78560c4bcd6de77e944f78e526f 100644 --- a/higgs_dna/workflows/__init__.py +++ b/higgs_dna/workflows/__init__.py @@ -8,6 +8,7 @@ from higgs_dna.workflows.particleLevel import ParticleLevelProcessor from higgs_dna.workflows.top import TopProcessor from higgs_dna.workflows.Zmmy import ZmmyProcessor, ZmmyHist, ZmmyZptHist from higgs_dna.workflows.hpc_processor import HplusCharmProcessor +from higgs_dna.workflows.zee_processor import ZeeProcessor from higgs_dna.workflows.lowmass import lowmassProcessor workflows = {} @@ -21,6 +22,7 @@ workflows["zmmy"] = ZmmyProcessor workflows["zmmyHist"] = ZmmyHist workflows["zmmyZptHist"] = ZmmyZptHist workflows["hpc"] = HplusCharmProcessor +workflows["zee"] = ZeeProcessor workflows["lowmass"] = lowmassProcessor __all__ = ["workflows", "taggers", "DYStudiesProcessor"] diff --git a/higgs_dna/workflows/zee_processor.py b/higgs_dna/workflows/zee_processor.py new file mode 100644 index 0000000000000000000000000000000000000000..7e489bd9cdfadafd1bb2156ef1b2e3a1981ccba2 --- /dev/null +++ b/higgs_dna/workflows/zee_processor.py @@ -0,0 +1,571 @@ +from higgs_dna.workflows.base import HggBaseProcessor +from higgs_dna.systematics import object_systematics as available_object_systematics +from higgs_dna.systematics import object_corrections as available_object_corrections +from higgs_dna.systematics import weight_systematics as available_weight_systematics +from higgs_dna.systematics import weight_corrections as available_weight_corrections +from higgs_dna.selections.photon_selections import photon_preselection +from higgs_dna.selections.lumi_selections import select_lumis +from higgs_dna.tools.SC_eta import add_photon_SC_eta +from higgs_dna.tools.flow_corrections import calculate_flow_corrections +from higgs_dna.tools.EcalBadCalibCrystal_events import remove_EcalBadCalibCrystal_events +from higgs_dna.tools.sigma_m_tools import compute_sigma_m +from typing import Any, Dict, List, Optional +import awkward +import logging +import functools +import warnings +import numpy +import sys +from coffea.analysis_tools import Weights +from higgs_dna.selections.lepton_selections import select_electrons, select_muons +from higgs_dna.selections.jet_selections import select_jets, jetvetomap +from higgs_dna.tools.EELeak_region import veto_EEleak_flag +from copy import deepcopy +from higgs_dna.utils.misc_utils import choose_jet + +from higgs_dna.utils.dumping_utils import ( + diphoton_ak_array, + dump_ak_array, + diphoton_list_to_pandas, + dump_pandas, + get_obj_syst_dict, +) + +logger = logging.getLogger(__name__) + + +class ZeeProcessor(HggBaseProcessor): + def __init__( + self, + metaconditions: Dict[str, Any], + systematics: Dict[str, List[Any]] = None, + corrections: Optional[Dict[str, List[str]]] = None, + apply_trigger: bool = False, + output_location: Optional[str] = None, + taggers: Optional[List[Any]] = None, + trigger_group: str = ".*DoubleEG.*", + analysis: str = "mainAnalysis", + skipCQR: bool = False, + skipJetVetoMap: bool = False, + year: Optional[Dict[str, List[str]]] = None, + fiducialCuts: str = "classical", + doDeco: bool = False, + Smear_sigma_m: bool = False, + doFlow_corrections: bool = False, + output_format: str = "parquet" + ) -> None: + super().__init__( + metaconditions, + systematics=systematics, + corrections=corrections, + apply_trigger=apply_trigger, + output_location=output_location, + taggers=taggers, + trigger_group=".*DoubleEG.*", + analysis="Dielectron", + skipCQR=skipCQR, + skipJetVetoMap=skipJetVetoMap, + year=year if year is not None else {}, + fiducialCuts=fiducialCuts, + doDeco=doDeco, + Smear_sigma_m=Smear_sigma_m, + doFlow_corrections=doFlow_corrections, + output_format=output_format + ) + + # diphoton preselection cuts - Based on Dielectron trigger + self.min_pt_photon = 23.0 + self.min_pt_lead_photon = 12.0 + + def postprocess(self, accumulant: Dict[Any, Any]) -> Any: + pass + + def process(self, events: awkward.Array) -> Dict[Any, Any]: + dataset_name = events.metadata["dataset"] + + # data or monte carlo? + self.data_kind = "mc" if hasattr(events, "GenPart") else "data" + + # here we start recording possible coffea accumulators + # most likely histograms, could be counters, arrays, ... + histos_etc = {} + histos_etc[dataset_name] = {} + if self.data_kind == "mc": + histos_etc[dataset_name]["nTot"] = int( + awkward.num(events.genWeight, axis=0) + ) + histos_etc[dataset_name]["nPos"] = int(awkward.sum(events.genWeight > 0)) + histos_etc[dataset_name]["nNeg"] = int(awkward.sum(events.genWeight < 0)) + histos_etc[dataset_name]["nEff"] = int( + histos_etc[dataset_name]["nPos"] - histos_etc[dataset_name]["nNeg"] + ) + histos_etc[dataset_name]["genWeightSum"] = float( + awkward.sum(events.genWeight) + ) + else: + histos_etc[dataset_name]["nTot"] = int(len(events)) + histos_etc[dataset_name]["nPos"] = int(histos_etc[dataset_name]["nTot"]) + histos_etc[dataset_name]["nNeg"] = int(0) + histos_etc[dataset_name]["nEff"] = int(histos_etc[dataset_name]["nTot"]) + histos_etc[dataset_name]["genWeightSum"] = float(len(events)) + + # lumi mask + if self.data_kind == "data": + try: + lumimask = select_lumis(self.year[dataset_name][0], events, logger) + events = events[lumimask] + except: + logger.info( + f"[ lumimask ] Skip now! Unable to find year info of {dataset_name}" + ) + # apply jetvetomap: only retain events that without any jets in the EE leakage region + if not self.skipJetVetoMap: + events = jetvetomap( + events, logger, dataset_name, year=self.year[dataset_name][0] + ) + # metadata array to append to higgsdna output + metadata = {} + + if self.data_kind == "mc": + # Add sum of gen weights before selection for normalisation in postprocessing + metadata["sum_genw_presel"] = str(awkward.sum(events.genWeight)) + else: + metadata["sum_genw_presel"] = "Data" + + # apply filters and triggers + events = self.apply_filters_and_triggers(events) + + # remove events affected by EcalBadCalibCrystal + if self.data_kind == "data": + events = remove_EcalBadCalibCrystal_events(events) + + # Matching photons eta and phi to electrons + events["Photon"] = events.Photon[events.Photon.electronIdx > -1] + events = events[awkward.num(events.Photon) >= 2] + + photons = events.Photon + matched_electrons = events.Electron[photons.electronIdx] + + # Keeping the photon eta and phi + photons["phoeta"] = photons.eta + photons["phophi"] = photons.phi + + # Substituting the photon eta and phi with the matched electron eta and phi + photons["eta"] = matched_electrons.eta + photons["phi"] = matched_electrons.phi + + photons["ele_pt"] = matched_electrons.pt + photons["ele_energy"] = matched_electrons.energy + photons["ele_ecalEnergy"] = matched_electrons.ecalEnergy + photons["ele_ecalEnergyError"] = matched_electrons.ecalEnergyError + photons["ele_ScEta"] = matched_electrons.eta + matched_electrons.deltaEtaSC + + events.Photon = photons + + # we need ScEta for corrections and systematics, it is present in NanoAODv13+ and can be calculated using PV for older versions + events.Photon = add_photon_SC_eta(events.Photon, events.PV) + + # add veto EE leak branch for photons, could also be used for electrons + if ( + self.year[dataset_name][0] == "2022EE" + or self.year[dataset_name][0] == "2022postEE" + ): + events.Photon = veto_EEleak_flag(self, events.Photon) + + # read which systematics and corrections to process + try: + correction_names = self.corrections[dataset_name] + except KeyError: + correction_names = [] + try: + systematic_names = self.systematics[dataset_name] + except KeyError: + systematic_names = [] + + # If --Smear_sigma_m == True and no Smearing correction in .json for MC throws an error, since the pt scpectrum need to be smeared in order to properly calculate the smeared sigma_m_m + if ( + self.data_kind == "mc" + and self.Smear_sigma_m + and not ("Smearing" in correction_names) + ): + warnings.warn( + "Smearing should be specified in the corrections field in .json in order to smear the mass!" + ) + sys.exit(0) + + # Since now we are applying Smearing term to the sigma_m_over_m i added this portion of code + # specially for the estimation of smearing terms for the data events [data pt/energy] are not smeared! + if self.data_kind == "data" and self.Smear_sigma_m: + # Since we smear sigma_m also in data, we also need to take the smearing term + correction_name = "Smearing" + + logger.info( + f"\nApplying correction {correction_name} to dataset {dataset_name}\n" + ) + varying_function = available_object_corrections[correction_name] + events = varying_function(events=events, year=self.year[dataset_name][0]) + + for correction_name in correction_names: + if correction_name in available_object_corrections.keys(): + logger.info( + f"Applying correction {correction_name} to dataset {dataset_name}" + ) + varying_function = available_object_corrections[correction_name] + events = varying_function( + events=events, year=self.year[dataset_name][0] + ) + elif correction_name in available_weight_corrections: + # event weight corrections will be applied after photon preselection / application of further taggers + continue + else: + # may want to throw an error instead, needs to be discussed + warnings.warn(f"Could not process correction {correction_name}.") + continue + + original_photons = events.Photon + # NOTE: jet jerc systematics are added in the correction functions and handled later + original_jets = events.Jet + + # Computing the normalizing flow correction + if self.data_kind == "mc" and self.doFlow_corrections: + + # Applyting the Flow corrections to all photons before pre-selection + counts = awkward.num(original_photons) + corrected_inputs,var_list = calculate_flow_corrections(original_photons, events, self.meta["flashggPhotons"]["flow_inputs"], self.meta["flashggPhotons"]["Isolation_transform_order"], year=self.year[dataset_name][0]) + + # Store the raw nanoAOD value and update photon ID MVA value for preselection + original_photons["mvaID_nano"] = original_photons["mvaID"] + + # Store the raw values of the inputs and update the input values with the corrections since some variables used in the preselection + for i in range(len(var_list)): + original_photons["raw_" + str(var_list[i])] = original_photons[str(var_list[i])] + original_photons[str(var_list[i])] = awkward.unflatten(corrected_inputs[:,i] , counts) + + original_photons["mvaID"] = awkward.unflatten(self.add_photonid_mva_run3(original_photons, events), counts) + + # systematic object variations + for systematic_name in systematic_names: + if systematic_name in available_object_systematics.keys(): + systematic_dct = available_object_systematics[systematic_name] + if systematic_dct["object"] == "Photon": + logger.info( + f"Adding systematic {systematic_name} to photons collection of dataset {dataset_name}" + ) + original_photons.add_systematic( + # passing the arguments here explicitly since I want to pass the events to the varying function. If there is a more elegant / flexible way, just change it! + name=systematic_name, + kind=systematic_dct["args"]["kind"], + what=systematic_dct["args"]["what"], + varying_function=functools.partial( + systematic_dct["args"]["varying_function"], + events=events, + year=self.year[dataset_name][0], + ) + # name=systematic_name, **systematic_dct["args"] + ) + # to be implemented for other objects here + elif systematic_name in available_weight_systematics: + # event weight systematics will be applied after photon preselection / application of further taggers + continue + else: + # may want to throw an error instead, needs to be discussed + warnings.warn( + f"Could not process systematic variation {systematic_name}." + ) + continue + + # Applying systematic variations + photons_dct = {} + photons_dct["nominal"] = original_photons + logger.debug(original_photons.systematics.fields) + for systematic in original_photons.systematics.fields: + for variation in original_photons.systematics[systematic].fields: + # deepcopy to allow for independent calculations on photon variables with CQR + photons_dct[f"{systematic}_{variation}"] = deepcopy( + original_photons.systematics[systematic][variation] + ) + + # NOTE: jet jerc systematics are added in the corrections, now extract those variations and create the dictionary + jerc_syst_list, jets_dct = get_obj_syst_dict(original_jets, ["pt", "mass"]) + # object systematics dictionary + logger.debug(f"[ jerc systematics ] {jerc_syst_list}") + + # Build the flattened array of all possible variations + variations_combined = [] + variations_combined.append(original_photons.systematics.fields) + # NOTE: jet jerc systematics are not added with add_systematics + variations_combined.append(jerc_syst_list) + # Flatten + variations_flattened = sum(variations_combined, []) # Begin with empty list and keep concatenating + # Attach _down and _up + variations = [item + suffix for item in variations_flattened for suffix in ['_down', '_up']] + # Add nominal to the list + variations.append('nominal') + logger.debug(f"[systematics variations] {variations}") + + for variation in variations: + photons, jets = photons_dct["nominal"], events.Jet + if variation == "nominal": + pass # Do nothing since we already get the unvaried, but nominally corrected objets above + elif variation in [*photons_dct]: # [*dict] gets the keys of the dict since Python >= 3.5 + photons = photons_dct[variation] + elif variation in [*jets_dct]: + jets = jets_dct[variation] + do_variation = variation # We can also simplify this a bit but for now it works + + if self.chained_quantile is not None: + photons = self.chained_quantile.apply(photons, events) + # recompute photonid_mva on the fly + if self.photonid_mva_EB and self.photonid_mva_EE: + photons = self.add_photonid_mva(photons, events) + + # photon preselection + photons = photon_preselection(self, photons, events, year=self.year[dataset_name][0], apply_electron_veto=False, IsFlag=True) + + # sort photons in each event descending in pt + # make descending-pt combinations of photons + photons = photons[awkward.argsort(photons.pt, ascending=False)] + photons["charge"] = awkward.zeros_like( + photons.pt + ) # added this because charge is not a property of photons in nanoAOD v11. We just assume every photon has charge zero... + diphotons = awkward.combinations( + photons, 2, fields=["pho_lead", "pho_sublead"] + ) + + # the remaining cut is to select the leading photons + # the previous sort assures the order + diphotons = diphotons[ + diphotons["pho_lead"].pt > self.min_pt_lead_photon + ] + + # now turn the diphotons into candidates with four momenta and such + diphoton_4mom = diphotons["pho_lead"] + diphotons["pho_sublead"] + diphotons["pt"] = diphoton_4mom.pt + diphotons["eta"] = diphoton_4mom.eta + diphotons["phi"] = diphoton_4mom.phi + diphotons["mass"] = diphoton_4mom.mass + diphotons["charge"] = diphoton_4mom.charge + + diphoton_pz = diphoton_4mom.z + diphoton_e = diphoton_4mom.energy + + diphotons["rapidity"] = 0.5 * numpy.log((diphoton_e + diphoton_pz) / (diphoton_e - diphoton_pz)) + + diphotons = awkward.with_name(diphotons, "PtEtaPhiMCandidate") + + # sort diphotons by pT + diphotons = diphotons[ + awkward.argsort(diphotons.pt, ascending=False) + ] + + # jet_variables + jets = awkward.zip( + { + "pt": jets.pt, + "eta": jets.eta, + "phi": jets.phi, + "mass": jets.mass, + "charge": awkward.zeros_like( + jets.pt + ), # added this because jet charge is not a property of photons in nanoAOD v11. We just need the charge to build jet collection. + "hFlav": jets.hadronFlavour + if self.data_kind == "mc" + else awkward.zeros_like(jets.pt), + "btagDeepFlav_B": jets.btagDeepFlavB, + "btagDeepFlav_CvB": jets.btagDeepFlavCvB, + "btagDeepFlav_CvL": jets.btagDeepFlavCvL, + "btagDeepFlav_QG": jets.btagDeepFlavQG, + "jetId": jets.jetId, + } + ) + jets = awkward.with_name(jets, "PtEtaPhiMCandidate") + + electrons = awkward.zip( + { + "pt": events.Electron.pt, + "eta": events.Electron.eta, + "phi": events.Electron.phi, + "mass": events.Electron.mass, + "charge": events.Electron.charge, + "cutBased": events.Electron.cutBased, + "mvaIso_WP90": events.Electron.mvaIso_WP90, + "mvaIso_WP80": events.Electron.mvaIso_WP80, + } + ) + electrons = awkward.with_name(electrons, "PtEtaPhiMCandidate") + + # Special cut for base workflow to replicate iso cut for electrons also for muons + events['Muon'] = events.Muon[events.Muon.pfRelIso03_all < 0.2] + + muons = awkward.zip( + { + "pt": events.Muon.pt, + "eta": events.Muon.eta, + "phi": events.Muon.phi, + "mass": events.Muon.mass, + "charge": events.Muon.charge, + "tightId": events.Muon.tightId, + "mediumId": events.Muon.mediumId, + "looseId": events.Muon.looseId, + "isGlobal": events.Muon.isGlobal, + } + ) + muons = awkward.with_name(muons, "PtEtaPhiMCandidate") + + # lepton cleaning + sel_electrons = electrons[ + select_electrons(self, electrons, diphotons) + ] + sel_muons = muons[select_muons(self, muons, diphotons)] + + # jet selection and pt ordering + jets = jets[ + select_jets(self, jets, diphotons, sel_muons, sel_electrons) + ] + jets = jets[awkward.argsort(jets.pt, ascending=False)] + + # adding selected jets to events to be used in ctagging SF calculation + events["sel_jets"] = jets + n_jets = awkward.num(jets) + Njets2p5 = awkward.num(jets[(jets.pt > 30) & (numpy.abs(jets.eta) < 2.5)]) + + first_jet_pt = choose_jet(jets.pt, 0, -999.0) + first_jet_eta = choose_jet(jets.eta, 0, -999.0) + first_jet_phi = choose_jet(jets.phi, 0, -999.0) + first_jet_mass = choose_jet(jets.mass, 0, -999.0) + first_jet_charge = choose_jet(jets.charge, 0, -999.0) + + second_jet_pt = choose_jet(jets.pt, 1, -999.0) + second_jet_eta = choose_jet(jets.eta, 1, -999.0) + second_jet_phi = choose_jet(jets.phi, 1, -999.0) + second_jet_mass = choose_jet(jets.mass, 1, -999.0) + second_jet_charge = choose_jet(jets.charge, 1, -999.0) + + diphotons["first_jet_pt"] = first_jet_pt + diphotons["first_jet_eta"] = first_jet_eta + diphotons["first_jet_phi"] = first_jet_phi + diphotons["first_jet_mass"] = first_jet_mass + diphotons["first_jet_charge"] = first_jet_charge + + diphotons["second_jet_pt"] = second_jet_pt + diphotons["second_jet_eta"] = second_jet_eta + diphotons["second_jet_phi"] = second_jet_phi + diphotons["second_jet_mass"] = second_jet_mass + diphotons["second_jet_charge"] = second_jet_charge + + diphotons["n_jets"] = n_jets + diphotons["Njets2p5"] = Njets2p5 + + diphotons = awkward.firsts(diphotons) + # set diphotons as part of the event record + events[f"diphotons_{do_variation}"] = diphotons + # annotate diphotons with event information + diphotons["event"] = events.event + diphotons["lumi"] = events.luminosityBlock + diphotons["run"] = events.run + # nPV just for validation of pileup reweighting + diphotons["nPV"] = events.PV.npvs + diphotons["fixedGridRhoAll"] = events.Rho.fixedGridRhoAll + # annotate diphotons with dZ information (difference between z position of GenVtx and PV) as required by flashggfinalfits + if self.data_kind == "mc": + diphotons["genWeight"] = events.genWeight + diphotons["dZ"] = events.GenVtx.z - events.PV.z + # Fill zeros for data because there is no GenVtx for data, obviously + else: + diphotons["dZ"] = awkward.zeros_like(events.PV.z) + + # drop events without a preselected diphoton candidate + # drop events without a tag, if there are tags + + selection_mask = ~awkward.is_none(diphotons) + diphotons = diphotons[selection_mask] + + # return if there is no surviving events + if len(diphotons) == 0: + logger.debug("No surviving events in this run, return now!") + return histos_etc + if self.data_kind == "mc": + # initiate Weight container here, after selection, since event selection cannot easily be applied to weight container afterwards + event_weights = Weights(size=len(events[selection_mask])) + + # corrections to event weights: + for correction_name in correction_names: + if correction_name in available_weight_corrections: + logger.info( + f"Adding correction {correction_name} to weight collection of dataset {dataset_name}" + ) + varying_function = available_weight_corrections[ + correction_name + ] + event_weights = varying_function( + events=events[selection_mask], + photons=events[f"diphotons_{do_variation}"][ + selection_mask + ], + weights=event_weights, + dataset_name=dataset_name, + year=self.year[dataset_name][0], + ) + + diphotons["weight_central"] = event_weights.weight() + # Store variations with respect to central weight + if do_variation == "nominal": + if len(event_weights.variations): + logger.info( + "Adding systematic weight variations to nominal output file." + ) + for modifier in event_weights.variations: + diphotons["weight_" + modifier] = event_weights.weight( + modifier=modifier + ) + + # Multiply weight by genWeight for normalisation in post-processing chain + event_weights._weight = ( + events["genWeight"][selection_mask] + * diphotons["weight_central"] + ) + diphotons["weight"] = event_weights.weight() + + # Add weight variables (=1) for data for consistent datasets + else: + diphotons["weight_central"] = awkward.ones_like( + diphotons["event"] + ) + diphotons["weight"] = awkward.ones_like(diphotons["event"]) + + # Compute and store the different variations of sigma_m_over_m + diphotons = compute_sigma_m(diphotons, processor='base', flow_corrections=self.doFlow_corrections, smear=self.Smear_sigma_m) + + if self.output_location is not None: + if self.output_format == "root": + df = diphoton_list_to_pandas(self, diphotons) + else: + akarr = diphoton_ak_array(self, diphotons) + + # Remove fixedGridRhoAll from photons to avoid having event-level info per photon + akarr = akarr[ + [ + field + for field in akarr.fields + if "lead_fixedGridRhoAll" not in field + ] + ] + + fname = ( + events.behavior[ + "__events_factory__" + ]._partition_key.replace("/", "_") + + ".%s" % self.output_format + ) + subdirs = [] + if "dataset" in events.metadata: + subdirs.append(events.metadata["dataset"]) + subdirs.append(do_variation) + if self.output_format == "root": + dump_pandas(self, df, fname, self.output_location, subdirs) + else: + dump_ak_array( + self, akarr, fname, self.output_location, metadata, subdirs, + ) + + return histos_etc