-
Notifications
You must be signed in to change notification settings - Fork 2
/
common.py
122 lines (107 loc) · 4.36 KB
/
common.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
from coffea.nanoevents import DelphesSchema
import numpy as np
import numba as nb
from numpy.typing import NDArray
import awkward as ak
from coffea.nanoevents.methods import vector
from coffea.nanoevents.methods.delphes import behavior, _set_repr_name, Particle
DelphesSchema.mixins.update({
"ParticleFlowCandidate": "Particle",
"FatJet": "Jet",
"GenFatJet": "Jet",
"DarkHadronJet": "Jet",
})
# workaround for https://cp3.irmp.ucl.ac.be/projects/delphes/ticket/1170
# manually fix mass units
@ak.mixin_class(behavior)
class GenParticle(Particle):
@property
def mass(self):
return self["Mass"]*0.001
_set_repr_name("GenParticle")
# propagate usage to schema, only for generator particles
DelphesSchema.mixins.update({
"GenParticle": "GenParticle",
"GenCandidate": "GenParticle",
"DarkHadronCandidate": "GenParticle",
})
class DelphesSchema2(DelphesSchema):
jet_const_pairs = {
"FatJet" : "ParticleFlowCandidate",
"Jet" : "ParticleFlowCandidate",
"DarkHadronJet" : "DarkHadronCandidate",
"GenFatJet" : "GenCandidate",
"GenJet" : "GenCandidate",
}
# avoid weird error when adding constituents
def __init__(self, base_form):
for key in list(base_form["contents"].keys()):
if "fBits" in key:
base_form["contents"].pop(key, None)
super().__init__(base_form)
# ignore unnecessary warning
from numba.core.errors import NumbaTypeSafetyWarning
import warnings
warnings.simplefilter('ignore',category=NumbaTypeSafetyWarning)
# optimized kernel for jet:constituent matching within an event
@nb.njit("i8[:](i8[:],u4[:])")
def get_constituents_kernel(jet_refs: NDArray[np.int64], cand_ids: NDArray[np.uint32]) -> NDArray[np.int64]:
# get hash table mapping global index : global unique ID
hash_table = {k:v for v,k in enumerate(cand_ids)}
# apply hash map
output = [hash_table[ref] for ref in jet_refs]
return np.asarray(output)
# apply kernel to events
def get_constituents(events, jetsname, candsname):
output = []
for jets,cands in zip(events[jetsname], events[candsname]):
indices = get_constituents_kernel(ak.flatten(jets.Constituents.refs).to_numpy(), cands.fUniqueID.to_numpy()) if jets is not None else []
if len(indices)==0:
unflattened = None
else:
unflattened = ak.unflatten(cands[indices],ak.count(jets.Constituents.refs,axis=1),behavior=cands.behavior)[None]
output.append(unflattened)
# very important for performance to call ak.concatenate only once at the end
return ak.with_name(ak.concatenate(output), DelphesSchema2.mixins[candsname])
def init_constituents(events):
for jet,const in DelphesSchema2.jet_const_pairs.items():
events[jet,"ConstituentsOrig"] = events[jet,"Constituents"]
events[jet,"Constituents"] = get_constituents(events,jet,const)
return events
# helper to test that all jet constituents were found
def sum_4vec(vec):
summed_vec = {
"t": ak.sum(vec.energy,axis=-1),
"x": ak.sum(vec.px,axis=-1),
"y": ak.sum(vec.py,axis=-1),
"z": ak.sum(vec.pz,axis=-1),
}
return ak.zip(summed_vec,with_name="LorentzVector")
def test_constituents(events):
for jet in DelphesSchema2.jet_const_pairs:
check_jets = sum_4vec(events[jet,"Constituents"])
print(jet, check_jets.mass-events[jet].mass)
def load_sample(sample,helper=None,schema=DelphesSchema,with_constituents=False):
from coffea.nanoevents import NanoEventsFactory
path = f'models/{sample["model"]}'
if helper is None:
from svjHelper import svjHelper
sample["helper"] = svjHelper.build(f'{path}/config.py')
else:
sample["helper"] = helper
metadict = sample["helper"].metadata()
metadict["dataset"] = sample["name"]
sample["events"] = load_events(f'{path}/events.root',schema=schema,metadict=metadict,with_constituents=with_constituents)
def load_events(filename,schema=DelphesSchema,metadict=None,with_constituents=False):
from coffea.nanoevents import NanoEventsFactory
if with_constituents and schema==DelphesSchema:
schema = DelphesSchema2
events = NanoEventsFactory.from_root(
file=filename,
treepath="Delphes",
schemaclass=schema,
metadata=metadict,
).events()
if with_constituents:
events = init_constituents(events)
return events