13 Utilities for PXD calibration and performance study
17 from ROOT
import Belle2
23 def get_name(self, separator="_"):
25 Get PXD module name with a specific format
28 separator (str): separator between different (layer/ladder/sensor) numbers.
30 PXD module name, e.g., 1_1_1
32 return f
"{self.getLayerNumber()}{separator}{self.getLadderNumber()}{separator}{self.getSensorNumber()}"
37 Get PXD module id: layer_id * 1000 + ladder_id * 10 + sensor_id
41 PXD module id, e.g., 1011
43 return self.getLayerNumber() * 1000 + self.getLadderNumber() * 10 + self.getSensorNumber()
47 def graph_append(self, x, y):
49 Append a point to TGraph
52 x (float): x coordinate of the point
53 y (float): y coordinate of the point
55 self.SetPoint(self.GetN(), x, y)
59 Belle2.VxdID.get_name = get_name
60 Belle2.VxdID.get_pxdid = get_pxdid
62 ROOT.TGraph.append = graph_append
76 sensorID_list = tuple([
Belle2.VxdID(label)
for label
in sensor_labels])
77 nsensors = len(sensorID_list)
86 ROOT.kRed + 1, ROOT.kOrange + 1, ROOT.kYellow - 3, ROOT.kSpring + 5, ROOT.kGreen + 3,
87 ROOT.kCyan - 6, ROOT.kAzure - 5, ROOT.kAzure - 6, ROOT.kBlue + 3, ROOT.kViolet - 1])
95 def get_sensor_graphs(ytitle=""):
97 Create TGraphs and related TLegends
99 ytitle (str): Title of the y-axis
101 A dictionary using sensorID as key and TGraph as value. A special key "TLegends"
102 is used for the list of TLegend objects for drawing.
107 xlegl = [0.60, 0.57, 0.78, 0.75]
108 xlegr = [0.72, 0.65, 0.90, 0.83]
111 Display format (content in a TLegend is bracketed):
112 (marker1 /) (marker2 label1/label2) (marker3 /) (markter4 label3/label4)
114 legs.append(ROOT.TLegend(xlegl[i], 0.62, xlegr[i], 0.85))
115 legs[i].SetFillStyle(0)
116 legs[i].SetBorderSize(0)
117 legs[i].SetTextFont(43)
118 legs[i].SetTextSize(18)
120 for i, sensorID
in enumerate(sensorID_list):
121 agraph = ROOT.TGraph()
123 agraph.SetName(sensorID.get_name())
124 agraph.GetXaxis().SetTitle(
'run #')
125 agraph.GetYaxis().SetTitle(ytitle)
126 agraph.GetYaxis().SetTitleOffset(0.9)
127 agraph.SetLineColor(colors[int(i / 2)])
128 agraph.SetLineWidth(4)
129 agraph.SetMarkerColor(colors[int(i / 2)])
130 agraph.SetMarkerStyle(26
if i % 2
else 24)
131 graphs[sensorID.getID()] = agraph
139 legs[i1].AddEntry(agraph, sensor_labels[i - 1] +
' / ' + sensor_labels[i],
'p')
141 legs[i2].AddEntry(agraph,
' / ',
'p')
142 graphs[
"TLegends"] = legs
148 name="MaskedPixels", title="Masked Pixels", ztitle="isMasked", run=0,
149 nUCells=nUCells, nVCells=nVCells, sensorID_list=sensorID_list
152 Create TH2F objects for saving pixel map
154 name (str): Name of a histogram
156 ztitle (str): Label for z-axis
157 run (int): run number of the map, to be appended into the title
158 sensorID_list (list): List of sensorID objects for which histograms are created.
160 A dictionary using sensorID as key and TH2F as value.
163 for sensorID
in sensorID_list:
164 hname = name +
"_{}_run_{:d}".format(sensorID.get_name(), run)
165 htitle = title +
" Sensor={:d} Run={:d}".format(sensorID.get_pxdid(), run)
166 h2 = ROOT.TH2F(hname, htitle, nUCells, 0, nUCells, nVCells, 0, nVCells)
167 h2.GetXaxis().SetTitle(
"uCell")
168 h2.GetYaxis().SetTitle(
"vCell")
169 h2.GetZaxis().SetTitle(ztitle)
171 hists[sensorID.getID()] = h2
175 def df_calculate_eff(df, num="nTrackClusters", den="nTrackPoints", output_var="eff"):
177 Efficiency calculation with asymmetric errors for pandas DataFrame
180 num (str): column used as the numerator
181 den (str): column used as the denominator
182 output_var (str): column for saving efficiency
184 nums = df[num].to_numpy()
185 dens = df[den].to_numpy()
186 from root_numpy
import array2hist
188 assert len(nums) == len(dens),
"len(numerators) != len(denominators)"
189 h_num = ROOT.TH1I(
"h_num",
"h_num", nBins, 0, nBins)
190 h_den = ROOT.TH1I(
"h_den",
"h_den", nBins, 0, nBins)
191 array2hist(nums, h_num)
192 array2hist(dens, h_den)
193 h_eff = ROOT.TEfficiency(h_num, h_den)
194 df[output_var] = df[num]/df[den]
195 df[output_var+
"_err_low"] = [h_eff.GetEfficiencyErrorLow(i+1)
for i
in range(nBins)]
196 df[output_var+
"_err_up"] = [h_eff.GetEfficiencyErrorUp(i+1)
for i
in range(nBins)]
199 def getH1Sigma68(h1, fill_random=False):
201 Helper function to get sigma68 from TH1
203 h1 (ROOT.TH1): TH1 object from ROOT
204 fill_random (bool): Flag to call TH1.FillRandom
208 qs = np.array([0., 0.])
209 probs = np.array([0.16, 0.84])
213 h1tmp.FillRandom(h1, int(h1.GetEffectiveEntries()))
214 h1tmp.GetQuantiles(2, qs, probs)
216 h1.GetQuantiles(2, qs, probs)
220 def getH1Sigma68WithError(h1, n=300):
222 Helper function to get sigma68 and its error using TH1.FillRandom
224 h1 (ROOT.TH1): TH1 object from ROOT
225 n (int): number of randomly generated histograms for the estimation
227 estimatd sigma68 and its standard deviation
229 results = np.array([getH1Sigma68(h1, fill_random=
True)
for i
in range(n)])
231 return getH1Sigma68(h1), results.std()
234 def getSigma68(array):
236 Helper function to get sigma68 and its error using numpy.array
238 array (numpy.array): target array
242 q1 = np.quantile(array, 0.16, axis=0)
243 q2 = np.quantile(array, 0.84, axis=0)
247 def getSigma68WithError(array, n=300):
249 Helper function to get sigma68 and its error using numpy.array
251 array (numpy.array): target array
252 n (int): number of bootstrap drawings
254 estimatd sigma68 and its standard deviation
256 bs = np.random.choice(array, (array.shape[0], n))
257 results = getSigma68(bs)
259 return getSigma68(a), results.std()
263 pd.DataFrame.calculate_eff = df_calculate_eff
266 latex_l = ROOT.TLatex()
267 latex_l.SetTextFont(43)
269 latex_l.SetTextSize(24)
270 latex_ls = ROOT.TLatex()
271 latex_ls.SetTextFont(43)
273 latex_ls.SetTextSize(19)
274 latex_r = ROOT.TLatex()
275 latex_r.SetTextFont(43)
277 latex_r.SetTextSize(24)
278 latex_r.SetTextAlign(31)
Class to uniquely identify a any structure of the PXD and SVD.