-
Notifications
You must be signed in to change notification settings - Fork 329
/
Copy pathdatamodel.py
150 lines (127 loc) · 5.87 KB
/
datamodel.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from PhysicsTools.NanoAODTools.postprocessing.framework.treeReaderArrayTools import InputTree
import ROOT
import math
ROOT.PyConfig.IgnoreCommandLineOptions = True
statusflags = { # GenPart_statusFlags, stored bitwise (powers of 2):
# https://github.com/cms-sw/cmssw/edit/master/PhysicsTools/NanoAOD/python/genparticles_cff.py
# https://cms-nanoaod-integration.web.cern.ch/integration/master-106X/mc106Xul18_doc.html#GenPart
'isPrompt': (1 << 0), 'fromHardProcess': (1 << 8),
'isDecayedLeptonHadron': (1 << 1), 'isHardProcessTauDecayProduct': (1 << 9),
'isTauDecayProduct': (1 << 2), 'isDirectHardProcessTauDecayProduct': (1 << 10),
'isPromptTauDecayProduct': (1 << 3), 'fromHardProcessBeforeFSR': (1 << 11),
'isDirectTauDecayProduct': (1 << 4), 'isFirstCopy': (1 << 12),
'isDirectPromptTauDecayProduct': (1 << 5), 'isLastCopy': (1 << 13),
'isDirectHadronDecayProduct': (1 << 6), 'isLastCopyBeforeFSR': (1 << 14),
'isHardProcess': (1 << 7),
}
class Event:
"""Class that allows seeing an entry of a PyROOT TTree as an Event"""
def __init__(self, tree, entry):
self._tree = tree
self._entry = entry
self._tree.gotoEntry(entry)
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
return self._tree.readBranch(name)
def __getitem__(self, attr):
return self.__getattr__(attr)
def eval(self, expr):
"""Evaluate an expression, as TTree::Draw would do.
This is added for convenience, but it may perform poorly and the implementation is not bulletproof,
so it's better to rely on reading values, collections or objects directly
"""
if not hasattr(self._tree, '_exprs'):
self._tree._exprs = {}
# remove useless warning about EvalInstance()
import warnings
warnings.filterwarnings(action='ignore', category=RuntimeWarning,
message='creating converter for unknown type "const char\*\*"$')
warnings.filterwarnings(action='ignore', category=RuntimeWarning,
message='creating converter for unknown type "const char\*\[\]"$')
if expr not in self._tree._exprs:
formula = ROOT.TTreeFormula(expr, expr, self._tree)
if formula.IsInteger():
formula.go = formula.EvalInstance64
else:
formula.go = formula.EvalInstance
self._tree._exprs[expr] = formula
# force sync, to be safe
self._tree.GetEntry(self._entry)
self._tree.entry = self._entry
# self._tree._exprs[expr].SetQuickLoad(False)
else:
self._tree.gotoEntry(self._entry)
formula = self._tree._exprs[expr]
if "[" in expr: # unclear why this is needed, but otherwise for some arrays x[i] == 0 for all i > 0
formula.GetNdata()
return formula.go()
class Object:
"""Class that allows seeing a set branches plus possibly an index as an Object"""
def __init__(self, event, prefix, index=None):
self._event = event
self._prefix = prefix + "_"
self._index = index
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
if name[:2] == "__" and name[-2:] == "__":
raise AttributeError
val = getattr(self._event, self._prefix + name)
if self._index != None:
val = val[self._index]
# convert char to integer number
val = ord(val) if type(val) == str else val
self.__dict__[name] = val # cache
return val
def __getitem__(self, attr):
return self.__getattr__(attr)
def p4(self, corr_pt=None):
"""Create TLorentzVector for this particle."""
ret = ROOT.TLorentzVector()
if corr_pt == None:
ret.SetPtEtaPhiM(self.pt, self.eta, self.phi, self.mass)
else:
ret.SetPtEtaPhiM(corr_pt, self.eta, self.phi, self.mass)
return ret
def DeltaR(self, other):
if isinstance(other, ROOT.TLorentzVector):
deta = abs(other.Eta() - self.eta)
dphi = abs(other.Phi() - self.phi)
else:
deta = abs(other.eta - self.eta)
dphi = abs(other.phi - self.phi)
while dphi > math.pi:
dphi = abs(dphi - 2 * math.pi)
return math.sqrt(dphi**2 + deta**2)
def statusflag(self, flag):
"""Find if bit for statusflag is set (for GenPart only)."""
return (self.statusFlags & statusflags[flag])==statusflags[flag]
def subObj(self, prefix):
return Object(self._event, self._prefix + prefix)
def __repr__(self):
return ("<%s[%s]>" % (self._prefix[:-1], self._index)) if self._index != None else ("<%s>" % self._prefix[:-1])
def __str__(self):
return self.__repr__()
class Collection:
def __init__(self, event, prefix, lenVar=None):
self._event = event
self._prefix = prefix
if lenVar != None:
self._len = getattr(event, lenVar)
else:
self._len = getattr(event, "n" + prefix)
self._cache = {}
def __getitem__(self, index):
if type(index) == int and index in self._cache:
return self._cache[index]
if index >= self._len:
raise IndexError("Invalid index %r (len is %r) at %s" % (index, self._len, self._prefix))
elif index < 0:
raise IndexError("Invalid index %r (negative) at %s" % (index, self._prefix))
ret = Object(self._event, self._prefix, index=index)
if type(index) == int:
self._cache[index] = ret
return ret
def __len__(self):
return self._len