def get_objects(ievent): visible_momentum = MadMinerParticle() for p in ( electrons_all_events[ievent] + jets_all_events[ievent] + muons_all_events[ievent] + leptons_all_events[ievent] ): visible_momentum += p all_momentum = visible_momentum + met_all_events[ievent][0] objects = math_commands() objects.update( { "e": electrons_all_events[ievent], "j": jets_all_events[ievent], "a": photons_all_events[ievent], "mu": muons_all_events[ievent], "l": leptons_all_events[ievent], "met": met_all_events[ievent][0], "visible": visible_momentum, "all": all_momentum, "boost_to_com": lambda momentum: momentum.boost(all_momentum.boost_vector()), } ) return objects
def _get_particles_charged(tree, name, mass, pdgid_positive_charge, pt_min, eta_max): pts = tree.array(f"{name}.PT") etas = tree.array(f"{name}.Eta") phis = tree.array(f"{name}.Phi") charges = tree.array(f"{name}.Charge") all_particles = [] for ievent in range(len(pts)): event_particles = [] for pt, eta, phi, charge in zip(pts[ievent], etas[ievent], phis[ievent], charges[ievent]): if pt_min is not None and pt < pt_min: continue if eta_max is not None and abs(eta) > eta_max: continue pdgid = pdgid_positive_charge if charge >= 0.0 else -pdgid_positive_charge particle = MadMinerParticle.from_rhophietatau(pt, phi, eta, mass) particle.set_pdgid(pdgid) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_photons(tree, pt_min, eta_max): pts = tree.array("Photon.PT") etas = tree.array("Photon.Eta") phis = tree.array("Photon.Phi") es = tree.array("Photon.E") all_particles = [] for ievent in range(len(pts)): event_particles = [] for pt, eta, phi, e in zip(pts[ievent], etas[ievent], phis[ievent], es[ievent]): if pt_min is not None and pt < pt_min: continue if eta_max is not None and abs(eta) > eta_max: continue particle = MadMinerParticle.from_rhophietat(pt, phi, eta, e) particle.set_pdgid(22) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_truth(tree, pt_min, eta_max, included_pdgids=None): es = tree.array("Particle.E") pts = tree.array("Particle.PT") etas = tree.array("Particle.Eta") phis = tree.array("Particle.Phi") charges = tree.array("Particle.Charge") pdgids = tree.array("Particle.PID") all_particles = [] for ievent in range(len(pts)): event_particles = [] for e, pt, eta, phi, pdgid in zip(es[ievent], pts[ievent], etas[ievent], phis[ievent], pdgids[ievent]): if pt_min is not None and pt < pt_min: continue if eta_max is not None and abs(eta) > eta_max: continue if (included_pdgids is not None) and (pdgid not in included_pdgids): continue particle = MadMinerParticle.from_rhophietat(pt, phi, eta, e) particle.set_pdgid(pdgid) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _parse_xml_event(event, sampling_benchmark): # Initialize weights and momenta weights = OrderedDict() particles = [] global_event_data = {} # Split kinematics part in tag line and momenta event_text = event.text tag_line = None particle_lines = [] for line in event_text.splitlines(): elements = line.split() if len(elements) < 2: continue if tag_line is None: tag_line = elements else: particle_lines.append(elements) # Parse tag assert tag_line is not None global_event_data["n_particles"] = float(tag_line[0]) weights[sampling_benchmark] = float(tag_line[2]) global_event_data["scale"] = float(tag_line[3]) global_event_data["alpha_qed"] = float(tag_line[4]) global_event_data["alpha_qcd"] = float(tag_line[5]) # Parse momenta for elements in particle_lines: if len(elements) < 10: continue status = int(elements[1]) if elements[0] == "#aMCatNLO": elements = elements[1:] if status == 1: pdgid = int(elements[0]) px = float(elements[6]) py = float(elements[7]) pz = float(elements[8]) e = float(elements[9]) spin = float(elements[12]) particle = MadMinerParticle() particle.setpxpypze(px, py, pz, e) particle.set_pdgid(pdgid) particle.set_spin(spin) particles.append(particle) # Weights if event.find("rwgt") is not None: for weight in event.find("rwgt").findall("wgt"): weight_id, weight_value = weight.attrib["id"], float(weight.text) weights[weight_id] = weight_value return particles, weights, global_event_data
def _get_particles_truth_jets(tree, pt_min, eta_max): pts = tree.array("GenJet.PT") etas = tree.array("GenJet.Eta") phis = tree.array("GenJet.Phi") masses = tree.array("GenJet.Mass") all_particles = [] for ievent in range(len(pts)): event_particles = [] for pt, eta, phi, mass in zip(pts[ievent], etas[ievent], phis[ievent], masses[ievent]): if pt_min is not None and pt < pt_min: continue if eta_max is not None and abs(eta) > eta_max: continue particle = MadMinerParticle() particle.setptetaphim(pt, eta, phi, mass) particle.set_pdgid(9) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_truth_leptons(tree, pt_min_e, eta_max_e, pt_min_mu, eta_max_mu): es = tree.array("Particle.E") pts = tree.array("Particle.PT") etas = tree.array("Particle.Eta") phis = tree.array("Particle.Phi") charges = tree.array("Particle.Charge") pdgids = tree.array("Particle.PID") all_particles = [] for ievent in range(len(pts)): event_particles = [] for e, pt, eta, phi, pdgid in zip(es[ievent], pts[ievent], etas[ievent], phis[ievent], pdgids[ievent]): if pdgid not in [11, 13, -11, -13]: continue if pdgid in [11, -11] and (pt_min_e is not None and pt < pt_min_e): continue if pdgid in [11, -11] and (eta_max_e is not None and abs(eta) > eta_max_e): continue if pdgid in [13, -13] and (pt_min_mu is not None and pt < pt_min_mu): continue if pdgid in [13, -13] and (eta_max_mu is not None and abs(eta) > eta_max_mu): continue particle = MadMinerParticle() particle.setptetaphie(pt, eta, phi, e) particle.set_pdgid(pdgid) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_met(tree): mets = tree.array("MissingET.MET") phis = tree.array("MissingET.Phi") all_particles = [] for ievent in range(len(mets)): event_particles = [] for met, phi in zip(mets[ievent], phis[ievent]): particle = MadMinerParticle.from_rhophietatau(met, phi, 0.0, 0.0) particle.set_pdgid(0) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_truth_jets(tree, pt_min, eta_max): pts = tree.array("GenJet.PT") etas = tree.array("GenJet.Eta") phis = tree.array("GenJet.Phi") masses = tree.array("GenJet.Mass") try: tau_tags = tree.array("GenJet.TauTag") except: logger.warning( "Did not find tau-tag information for GenJets in Delphes ROOT file." ) tau_tags = [0 for _ in range(len(pts))] try: b_tags = tree.array("GenJet.BTag") except: logger.warning( "Did not find b-tag information for GenJets in Delphes ROOT file.") b_tags = [0 for _ in range(len(pts))] all_particles = [] for ievent in range(len(pts)): event_particles = [] for pt, eta, phi, mass, tau_tag, b_tag in zip( pts[ievent], etas[ievent], phis[ievent], masses[ievent], tau_tags[ievent], b_tags[ievent]): if pt_min is not None and pt < pt_min: continue if eta_max is not None and abs(eta) > eta_max: continue particle = MadMinerParticle() particle.setptetaphim(pt, eta, phi, mass) particle.set_pdgid(9) particle.set_tags(tau_tag >= 1, b_tag >= 1, False) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_truth_leptons(tree, pt_min_e, eta_max_e, pt_min_mu, eta_max_mu): ids_e = {int(p.pdgid) for p in Particle.findall(pdg_name="e")} ids_mu = {int(p.pdgid) for p in Particle.findall(pdg_name="mu")} es = tree.array("Particle.E") pts = tree.array("Particle.PT") etas = tree.array("Particle.Eta") phis = tree.array("Particle.Phi") charges = tree.array("Particle.Charge") pdgids = tree.array("Particle.PID") all_particles = [] for ievent in range(len(pts)): event_particles = [] for e, pt, eta, phi, pdgid in zip(es[ievent], pts[ievent], etas[ievent], phis[ievent], pdgids[ievent]): if pdgid not in {*ids_e, *ids_mu}: continue if pdgid in ids_e and (pt_min_e is not None and pt < pt_min_e): continue if pdgid in ids_e and (eta_max_e is not None and abs(eta) > eta_max_e): continue if pdgid in ids_mu and (pt_min_mu is not None and pt < pt_min_mu): continue if pdgid in ids_mu and (eta_max_mu is not None and abs(eta) > eta_max_mu): continue particle = MadMinerParticle.from_rhophietat(pt, phi, eta, e) particle.set_pdgid(pdgid) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _get_particles_truth_met(tree): mets = tree.array("GenMissingET.MET") phis = tree.array("GenMissingET.Phi") all_particles = [] for ievent in range(len(mets)): event_particles = [] for met, phi in zip(mets[ievent], phis[ievent]): particle = MadMinerParticle() particle.setptetaphim(met, 0.0, phi, 0.0) particle.set_pdgid(0) event_particles.append(particle) all_particles.append(event_particles) return all_particles
def _parse_events_text(filename, sampling_benchmark): # Initialize weights and momenta weights = OrderedDict() particles = [] # Some tags so that we know where in the event we are do_tag = False do_momenta = False do_reweight = False # Event n_events = 0 reset_event = False # Loop through lines in Event with open(filename, "r") as file: for line in file.readlines(): # Clean up line try: line = line.split("#")[0] except: pass line = line.strip() elements = line.split() # Skip empty/commented out lines if len(line) == 0 or len(elements) == 0: continue # End of LHE file elif line == "</LesHouchesEvents>": return # Beginning of event elif line == "<event>": # Initialize weights and momenta weights = OrderedDict() particles = [] # Some tags so that we know where in the event we are do_tag = True do_momenta = False do_reweight = False # End of event elif line == "</event>": n_events += 1 if n_events % 10000 == 0: logger.debug("%s events parsed", n_events) yield particles, weights # Reset weights and momenta weights = OrderedDict() particles = [] # Some tags so that we know where in the event we are do_tag = False do_momenta = False do_reweight = False # Beginning of unimportant block elif line == "<mgrwt>": do_tag = False do_momenta = False do_reweight = False # Beginning of weight block elif line == "<rwgt>": do_tag = False do_momenta = False do_reweight = True # End of weight block elif line == "</rwgt>": do_tag = False do_momenta = False do_reweight = False # Read tag -> first weight elif do_tag: weights[sampling_benchmark] = float(elements[2]) do_tag = False do_momenta = True do_reweight = False # Read Momenta and store as 4-vector elif do_momenta: status = int(elements[1]) if status == 1: pdgid = int(elements[0]) px = float(elements[6]) py = float(elements[7]) pz = float(elements[8]) e = float(elements[9]) particle = MadMinerParticle() particle.setpxpypze(px, py, pz, e) particle.set_pdgid(pdgid) particles.append(particle) # Read reweighted weights elif do_reweight: rwgtid = line[line.find("<") + 1:line.find(">")].split("=")[1][1:-1] rwgtval = float(line[line.find(">") + 1:line.find("<", line.find("<") + 1)]) weights[rwgtid] = rwgtval
def _get_objects(particles): # Find visible particles electrons = [] muons = [] taus = [] photons = [] jets = [] leptons = [] neutrinos = [] unstables = [] invisibles = [] for particle in particles: pdgid = abs(particle.pdgid) if pdgid in [1, 2, 3, 4, 5, 6, 9, 21]: jets.append(particle) elif pdgid == 11: electrons.append(particle) leptons.append(particle) elif pdgid == 13: muons.append(particle) leptons.append(particle) elif pdgid == 15: taus.append(particle) elif pdgid == 22: photons.append(particle) elif pdgid in [12, 14, 16]: neutrinos.append(particle) invisibles.append(particle) elif pdgid in [23, 24, 25]: unstables.append(particle) else: logger.warning("Unknown particle with PDG id %s, treating as invisible!") invisibles.append(particle) # Sort by pT electrons = sorted(electrons, reverse=True, key=lambda x: x.pt) muons = sorted(muons, reverse=True, key=lambda x: x.pt) taus = sorted(taus, reverse=True, key=lambda x: x.pt) photons = sorted(photons, reverse=True, key=lambda x: x.pt) leptons = sorted(leptons, reverse=True, key=lambda x: x.pt) neutrinos = sorted(neutrinos, reverse=True, key=lambda x: x.pt) jets = sorted(jets, reverse=True, key=lambda x: x.pt) # MET visible_sum = MadMinerParticle() visible_sum.setpxpypze(0.0, 0.0, 0.0, 0.0) for particle in particles: pdgid = abs(particle.pdgid) if pdgid in [1, 2, 3, 4, 5, 6, 9, 11, 13, 15, 21, 22, 23, 24, 25]: visible_sum += particle met = MadMinerParticle() met.setpxpypze(-visible_sum.px, -visible_sum.px, 0.0, visible_sum.pt) # Build objects objects = math_commands() objects.update( { "p": particles, "e": electrons, "j": jets, "a": photons, "mu": muons, "tau": taus, "l": leptons, "met": met, "v": neutrinos, } ) return objects
def get_objects(ievent): visible_momentum = MadMinerParticle() for p in ( electrons_all_events[ievent] + jets_all_events[ievent] + muons_all_events[ievent] + photons_all_events[ievent] ): visible_momentum += p all_momentum = visible_momentum + met_all_events[ievent][0] objects = math_commands() objects.update( { "e": electrons_all_events[ievent], "j": jets_all_events[ievent], "a": photons_all_events[ievent], "mu": muons_all_events[ievent], "l": leptons_all_events[ievent], "met": met_all_events[ievent][0], "visible": visible_momentum, "all": all_momentum, "boost_to_com": lambda momentum: momentum.boost(all_momentum.boost_vector()), } ) ##################### PREPARE FUNCTIONS TO FIND BEST ZZ CANDIDATE ##################### #find same flavour opposite sign lepton pairs, #given the list of electrons or muons in an event #returns all possible SFOS pairs ([pos_idx, neg_idx],...) #which satisfy invariant mass cuts def find_Z_cand(cand_list, part_list): #positively and negatively charged leptons, and their idx_pos = [] cand_pos = [] idx_neg = [] cand_neg = [] for idx, (is_cand, part) in enumerate(zip(cand_list, part_list)): #checking if the lepton is a candidate if part.charge > 0: idx_pos.append(idx) cand_pos.append(is_cand) if part.charge < 0: idx_neg.append(idx) cand_neg.append(is_cand) z_cand_list = [] for candpair, pair in zip(itertools.product(cand_pos, cand_neg), itertools.product(idx_pos, idx_neg)): iscand = candpair[0]*candpair[1] #1 only if both are candidates #cut invariant mass invm = (part_list[pair[0]] + part_list[pair[1]]).m if invm < 120 and invm > 12: z_cand_list.append([pair[0],pair[1]]) return z_cand_list #[[pos_idx,neg_idx], [pos_idx,neg_idx], ...] #find pairs of SFOS pairs with no leptons in common #keep track of their different flavours #order of Z1 and Z2 is not important (each only counted once) def find_ZZ_cand(Z_cand_e_list, Z_cand_mu_list): ZZ_cand_ee = []; ZZ_cand_mm = []; ZZ_cand_em = [] #same flavour e for Zcand1,Zcand2 in itertools.product(Z_cand_e_list,Z_cand_e_list): if not any(x in Zcand1 for x in Zcand2): ZZ_cand_ee.append([Zcand1[0], Zcand1[1], Zcand2[0], Zcand2[1]]) #same flavour mu for Zcand1,Zcand2 in itertools.product(Z_cand_mu_list,Z_cand_mu_list): if not any(x in Zcand1 for x in Zcand2): ZZ_cand_mm.append([Zcand1[0], Zcand1[1], Zcand2[0], Zcand2[1]]) #different flavour for Zcand1,Zcand2 in itertools.product(Z_cand_e_list,Z_cand_mu_list): ZZ_cand_em.append([Zcand1[0], Zcand1[1], Zcand2[0], Zcand2[1]]) return ZZ_cand_ee, ZZ_cand_mm, ZZ_cand_em #return the list of particles in the correct order: #first list with flavour of first Z, second flavour of second Z def set_flavours_lists(part_list_e, part_list_mu, flavours): if flavours == "ee": part_list1 = part_list_e part_list2 = part_list_e elif flavours == "mm": part_list1 = part_list_mu part_list2 = part_list_mu elif flavours == "em": part_list1 = part_list_e part_list2 = part_list_mu elif flavours == "me": part_list1 = part_list_mu part_list2 = part_list_e else: print("set_flavours_lists: flavour not valid: choose between ee, mm, em") return return part_list1, part_list2 #return the leptons corresponding to the index in ZZcand #taking into account the flavours def ZZidx_to_leps(ZZcand, part_list_e, part_list_mu, flavours): part_list1, part_list2 = set_flavours_lists(part_list_e, part_list_mu, flavours) leps = [part_list1[ZZcand[0]], part_list1[ZZcand[1]], part_list2[ZZcand[2]], part_list2[ZZcand[3]]] return leps #reorder the two Z in each pair: first the one with closest mass to Z #keep track of the possible swapping in case of different flavours def Z1Z2_ordering(ZZcand, part_list_e, part_list_mu, flavours): leps = ZZidx_to_leps(ZZcand, part_list_e, part_list_mu, flavours) #compute invariant masses of each Z mz_p1 = (leps[0] + leps[1]).m mz_p2 = (leps[2] + leps[3]).m #order accordingly to closest mass to Z mass if min([mz_p1,mz_p2], key=lambda x:abs(x-91.2), default=-1) == mz_p1: z1z2 = ZZcand swapped = False else: z1z2 = [ZZcand[2],ZZcand[3],ZZcand[0],ZZcand[1]] swapped = True return(z1z2, swapped) #apply cuts to ZZ candidate #consider differently the case of same flavour ZZ def ZZ_cuts(ZZcand, part_list_e, part_list_mu, flavours, isSF): leps = ZZidx_to_leps(ZZcand, part_list_e, part_list_mu, flavours) #Z1 mass mz1 = (leps[0] + leps[1]).m if not mz1 > 40: #print("fail mz1 mass cut") return False #leptons pt leps_pt_sort = sorted(leps, key=lambda x:x.pt) if not (leps_pt_sort[3].pt > 20 and leps_pt_sort[2].pt > 10): #print("fail leptons pt cut") #print("leptons pt: ", [a.pt for a in leps_pt_sort]) return False #OS invariant mass (pos, neg, pos, neg) mza = (leps[0]+leps[3]).m mzb = (leps[1]+leps[2]).m mz2 = (leps[2]+leps[3]).m if not (mza > 4 and mzb > 4 and mz2 > 4): #print("fail os cut") return False #4l invariant mass m4l = (leps[0]+leps[1]+leps[2]+leps[3]).m #print("m4l: ", m4l) if not m4l>70: #print("fail m4l cut") return False #same flavour cut if isSF == True: mz_pdg = 91.2 mzab_ord = [mza, mzb] if min([mza,mzb], key=lambda x:abs(x-mz_pdg)) == mza else [mzb,mza] if (abs(mzab_ord[0]-mz_pdg) < abs(mz1-mz_pdg) and mzb < mzab_ord[0]): #print("fail SF cut") return False #if candidate passes all cuts return True #choose ZZ candidate for which Z1 has mass closest to Z #keep track of the flavour of Z1 and Z2 def choose_final_ZZ(ZZlist, part_list_e, part_list_mu, flavourslist): if len(ZZlist) == 1: return ZZlist[0], flavourslist[0] else: mz1list = [] for ZZcand, flavours in zip(ZZlist,flavourslist): part_list1, part_list2 = set_flavours_lists(part_list_e, part_list_mu, flavours) mz1 = (part_list1[ZZcand[0]] + part_list1[ZZcand[1]]).m mz1list.append(mz1) mz1min = min(mz1list, key=lambda x:abs(x-91.2)) idx = mz1list.index(mz1min) return ZZlist[idx], flavourslist[idx] ##################### MAIN CODE TO FIND BEST ZZ CANDIDATE ##################### #default values isZZcand = 0 lep1 = MadMinerParticle(); lep2 = MadMinerParticle(); lep3 = MadMinerParticle(); lep4 = MadMinerParticle() #list of candidates electrons/muons: 1 if candidate, 0 if not candidate_es = np.ones(len(objects["e"])) candidate_mus = np.ones(len(objects["mu"])) #print(len(objects["e"]), len(objects["mu"])) #find candidate leptons: eta and pt cuts (to be checked) for idx, el in enumerate(objects["e"]): if not (abs(el.eta) < 2.5 and el.pt > 7): candidate_es[idx] = 0 for idx, mu in enumerate(objects["mu"]): if not (abs(mu.eta) < 2.4 and mu.pt > 5): candidate_mus[idx] = 0 #find Z candidates e_Z_cand = find_Z_cand(candidate_es, objects["e"]) mu_Z_cand = find_Z_cand(candidate_mus, objects["mu"]) #find ZZ candidates ZZ_cand_ee_list, ZZ_cand_mm_list, ZZ_cand_em_list = find_ZZ_cand(e_Z_cand,mu_Z_cand) #initialise lists for ZZ candidates which pass cuts, and keep track of flavours ZZ_cands_final = [] ZZ_cands_final_flavours = [] #same flavours, electrons for ZZ_cand_ee in ZZ_cand_ee_list: ZZ_cand_ee, swapped = Z1Z2_ordering(ZZ_cand_ee, objects["e"], objects["mu"], flavours="ee") #apply cuts if ZZ_cuts(ZZ_cand_ee, objects["e"], objects["mu"], flavours="ee", isSF=True) == True: ZZ_cands_final.append(ZZ_cand_ee) ZZ_cands_final_flavours.append("ee") #same flavours, muons for ZZ_cand_mm in ZZ_cand_mm_list: ZZ_cand_mm, swapped = Z1Z2_ordering(ZZ_cand_mm, objects["e"], objects["mu"], flavours="mm") #apply cuts if ZZ_cuts(ZZ_cand_mm, objects["e"], objects["mu"], flavours="mm", isSF=True) == True: ZZ_cands_final.append(ZZ_cand_mm) ZZ_cands_final_flavours.append("mm") #different flavours for ZZ_cand_em in ZZ_cand_em_list: ZZ_cand_em, swapped = Z1Z2_ordering(ZZ_cand_em, objects["e"], objects["mu"], flavours="em") #apply cuts, careful when swapping em/me flavours_OF = "em" if swapped==False else "me" if ZZ_cuts(ZZ_cand_em, objects["e"], objects["mu"], flavours=flavours_OF, isSF=False) == True: ZZ_cands_final.append(ZZ_cand_em) ZZ_cands_final_flavours.append(flavours_OF) #find final best ZZ candidate #if more than one ZZ candidate is left: choose the one with Z1 mass closest to MZ def printlep(lep, name): #print(name, " = [", lep.px, ",", lep.py, ",", lep.pz, "]") print("[", lep.e,",", lep.px, ",", lep.py, ",", lep.pz, "]") return if len(ZZ_cands_final) > 0: isZZcand = 1 ZZfinal, flavours = choose_final_ZZ(ZZ_cands_final, objects["e"], objects["mu"], ZZ_cands_final_flavours) lep1, lep2, lep3, lep4 = ZZidx_to_leps(ZZfinal, objects["e"], objects["mu"], flavours) #print("chosen leptons: (flavours", flavours) #for lep, nlep in zip([lep1,lep2,lep3,lep4],["a","b","c","d"]): #printlep(lep, nlep) #print((lep1+lep2+lep3+lep4).m) #print((lep1+lep2).m, (lep3+lep4).m) #print("original leptons: e") #for idx, lep in enumerate(objects["e"]): #nlep = "ne" + str(idx) #printlep(lep, nlep) #print("original leptons: mu") #for idx, lep in enumerate(objects["mu"]): #nlep = "nmu" + str(idx) #printlep(lep, nlep) #logger.debug("Value of isZZcand: %d", isZZcand) #debugging #print(len(objects["e"]), len(objects["mu"])) #if(len(ZZ_cand_ee_list) + len(ZZ_cand_mm_list) + len(ZZ_cand_em_list)) >= 1: #print(len(objects["e"]), len(objects["mu"])) #if isZZcand != 1: #print("no ZZ cand found after cuts") #else: #print("ok candidate and found") #update objects dictionary objects.update( { "isZZcand": isZZcand, "lep1ZZ": lep1, "lep2ZZ": lep2, "lep3ZZ": lep3, "lep4ZZ": lep4, } ) return objects
def _get_objects(particles, particles_truth, met_resolution=None, global_event_data=None): # Find visible particles electrons = [] muons = [] taus = [] photons = [] jets = [] leptons = [] neutrinos = [] unstables = [] invisibles = [] for particle in particles: pdgid = abs(particle.pdgid) if pdgid in [1, 2, 3, 4, 5, 6, 9, 21]: if pdgid == 5: particle.set_tags(False, True, False) if pdgid == 6: particle.set_tags(False, False, True) jets.append(particle) elif pdgid == 11: electrons.append(particle) leptons.append(particle) elif pdgid == 13: muons.append(particle) leptons.append(particle) elif pdgid == 15: taus.append(particle) elif pdgid == 22: photons.append(particle) elif pdgid in [12, 14, 16]: neutrinos.append(particle) invisibles.append(particle) elif pdgid in [23, 24, 25]: unstables.append(particle) else: logger.warning("Unknown particle with PDG id %s, treating as invisible!") invisibles.append(particle) # Sort by pT electrons = sorted(electrons, reverse=True, key=lambda x: x.pt) muons = sorted(muons, reverse=True, key=lambda x: x.pt) taus = sorted(taus, reverse=True, key=lambda x: x.pt) photons = sorted(photons, reverse=True, key=lambda x: x.pt) leptons = sorted(leptons, reverse=True, key=lambda x: x.pt) neutrinos = sorted(neutrinos, reverse=True, key=lambda x: x.pt) jets = sorted(jets, reverse=True, key=lambda x: x.pt) # Sum over all particles ht = 0.0 visible_sum = MadMinerParticle.from_xyzt(0.0, 0.0, 0.0, 0.0) standard_ids = set(get_elementary_pdg_ids()) neutrino_ids = set( int(p.pdgid) for p in Particle.findall(lambda p: p.pdgid.is_sm_lepton and p.charge == 0) ) for particle in particles: if particle.pdgid in standard_ids and particle.pdgid not in neutrino_ids: visible_sum += particle ht += particle.pt # Soft noise if met_resolution is not None: noise_std = met_resolution[0] + met_resolution[1] * ht noise_x = np.random.normal(0.0, noise_std, size=None) noise_y = np.random.normal(0.0, noise_std, size=None) else: noise_x = 0.0 noise_y = 0.0 # MET met_x = -visible_sum.x + noise_x met_y = -visible_sum.y + noise_y met = MadMinerParticle.from_xyzt( x=met_x, y=met_y, z=0.0, t=(met_x ** 2 + met_y ** 2) ** 0.5, ) # Build objects objects = math_commands() objects.update( { "p": particles, "p_truth": particles_truth, "e": electrons, "j": jets, "a": photons, "mu": muons, "tau": taus, "l": leptons, "met": met, "v": neutrinos, } ) # Global event_data if global_event_data is not None: objects.update(global_event_data) return objects
def _get_objects(particles, met_resolution=None): # Find visible particles electrons = [] muons = [] taus = [] photons = [] jets = [] leptons = [] neutrinos = [] unstables = [] invisibles = [] for particle in particles: pdgid = abs(particle.pdgid) if pdgid in [1, 2, 3, 4, 5, 6, 9, 21]: jets.append(particle) elif pdgid == 11: electrons.append(particle) leptons.append(particle) elif pdgid == 13: muons.append(particle) leptons.append(particle) elif pdgid == 15: taus.append(particle) elif pdgid == 22: photons.append(particle) elif pdgid in [12, 14, 16]: neutrinos.append(particle) invisibles.append(particle) elif pdgid in [23, 24, 25]: unstables.append(particle) else: logger.warning( "Unknown particle with PDG id %s, treating as invisible!") invisibles.append(particle) # Sort by pT electrons = sorted(electrons, reverse=True, key=lambda x: x.pt) muons = sorted(muons, reverse=True, key=lambda x: x.pt) taus = sorted(taus, reverse=True, key=lambda x: x.pt) photons = sorted(photons, reverse=True, key=lambda x: x.pt) leptons = sorted(leptons, reverse=True, key=lambda x: x.pt) neutrinos = sorted(neutrinos, reverse=True, key=lambda x: x.pt) jets = sorted(jets, reverse=True, key=lambda x: x.pt) # Sum over all particles ht = 0.0 visible_sum = MadMinerParticle() visible_sum.setpxpypze(0.0, 0.0, 0.0, 0.0) for particle in particles: ht += particle.pt pdgid = abs(particle.pdgid) if pdgid in [1, 2, 3, 4, 5, 6, 9, 11, 13, 15, 21, 22, 23, 24, 25]: visible_sum += particle # Soft noise if met_resolution is not None: noise_std = met_resolution[0] + met_resolution[1] * ht noise_x = np.random.normal(0.0, noise_std, size=None) noise_y = np.random.normal(0.0, noise_std, size=None) else: noise_x = 0.0 noise_y = 0.0 # MET met_x = -visible_sum.px + noise_x met_y = -visible_sum.px + noise_y met = MadMinerParticle() met.setpxpypze(met_x, met_y, 0.0, (met_x**2 + met_y**2)**0.5) # Build objects objects = math_commands() objects.update({ "p": particles, "e": electrons, "j": jets, "a": photons, "mu": muons, "tau": taus, "l": leptons, "met": met, "v": neutrinos, }) return objects
def _smear_particles(particles, energy_resolutions, pt_resolutions, eta_resolutions, phi_resolutions): """ Applies smearing function to particles of one event """ # No smearing if any argument is None if energy_resolutions is None or pt_resolutions is None or eta_resolutions is None or phi_resolutions is None: return particles smeared_particles = [] for particle in particles: pdgid = particle.pdgid if (pdgid not in six.iterkeys(energy_resolutions) or pdgid not in six.iterkeys(pt_resolutions) or pdgid not in six.iterkeys(eta_resolutions) or pdgid not in six.iterkeys(phi_resolutions)): continue if None in energy_resolutions[pdgid] and None in pt_resolutions[pdgid]: raise RuntimeError( "Cannot derive both pT and energy from on-shell conditions!") # Minimum energy and pT m = particle.m min_e = 0.0 if None in pt_resolutions[pdgid]: min_e = m min_pt = 0.0 # Smear four-momenta e = None if None not in energy_resolutions[pdgid]: e = min_e - 1.0 while e <= min_e: e = _smear_variable(particle.e, energy_resolutions, pdgid) pt = None if None not in pt_resolutions[pdgid]: pt = min_pt - 1.0 while pt <= min_pt: pt = _smear_variable(particle.pt, pt_resolutions, pdgid) eta = _smear_variable(particle.eta, eta_resolutions, pdgid) phi = _smear_variable(particle.phi(), phi_resolutions, pdgid) while phi > 2.0 * np.pi: phi -= 2.0 * np.pi while phi < 0.0: phi += 2.0 * np.pi # Construct particle smeared_particle = MadMinerParticle() if None in energy_resolutions[pdgid]: # Calculate E from on-shell conditions smeared_particle.setptetaphim(pt, eta, phi, particle.m) elif None in pt_resolutions[pdgid]: # Calculate pT from on-shell conditions if e > m: pt = (e**2 - m**2)**0.5 / np.cosh(eta) else: pt = 0.0 smeared_particle.setptetaphie(pt, eta, phi, e) else: # Everything smeared manually smeared_particle.setptetaphie(pt, eta, phi, e) # PDG id (also sets charge) smeared_particle.set_pdgid(pdgid) smeared_particles.append(smeared_particle) return smeared_particles
def _get_particles_leptons(tree, pt_min_e, eta_max_e, pt_min_mu, eta_max_mu): pt_mu = tree.array("Muon.PT") eta_mu = tree.array("Muon.Eta") phi_mu = tree.array("Muon.Phi") charge_mu = tree.array("Muon.Charge") pt_e = tree.array("Electron.PT") eta_e = tree.array("Electron.Eta") phi_e = tree.array("Electron.Phi") charge_e = tree.array("Electron.Charge") all_particles = [] for ievent in range(len(pt_mu)): event_particles = [] # Combined muons and electrons event_pts = np.concatenate((pt_mu[ievent], pt_e[ievent])) event_etas = np.concatenate((eta_mu[ievent], eta_e[ievent])) event_phis = np.concatenate((phi_mu[ievent], phi_e[ievent])) event_masses = np.concatenate((0.105 * np.ones_like(pt_mu[ievent]), 0.000511 * np.ones_like(pt_e[ievent]))) event_charges = np.concatenate((charge_mu[ievent], charge_e[ievent])) event_pdgid_positive_charges = np.concatenate( (-13 * np.ones_like(pt_mu[ievent], dtype=np.int), -11 * np.ones_like(pt_e[ievent], dtype=np.int)) ) # Sort by descending pT order = np.argsort(-1.0 * event_pts, axis=None) event_pts = event_pts[order] event_etas = event_etas[order] event_phis = event_phis[order] # Create particles for pt, eta, phi, mass, charge, pdgid_positive_charge in zip( event_pts, event_etas, event_phis, event_masses, event_charges, event_pdgid_positive_charges ): pdgid = pdgid_positive_charge if charge >= 0.0 else -pdgid_positive_charge if abs(int(pdgid)) == 11: if pt_min_e is not None and pt < pt_min_e: continue if eta_max_e is not None and abs(eta) > eta_max_e: continue elif abs(int(pdgid)) == 13: if pt_min_mu is not None and pt < pt_min_mu: continue if eta_max_mu is not None and abs(eta) > eta_max_mu: continue else: logging.warning("Delphes ROOT file has lepton with PDG ID %s, ignoring it", pdgid) continue particle = MadMinerParticle() particle.setptetaphim(pt, eta, phi, mass) particle.set_pdgid(pdgid) event_particles.append(particle) all_particles.append(event_particles) return all_particles