21 <input>trackingEfficiency_pt_0.05GeV.root,
22 trackingEfficiency_pt_0.10GeV.root,trackingEfficiency_pt_0.25GeV.root,
23 trackingEfficiency_pt_0.40GeV.root,trackingEfficiency_pt_0.60GeV.root,
24 trackingEfficiency_pt_1.00GeV.root,trackingEfficiency_pt_1.50GeV.root,
25 trackingEfficiency_pt_2.00GeV.root,trackingEfficiency_pt_3.00GeV.root,
26 trackingEfficiency_pt_4.00GeV.root</input>
27 <output>TrackingValidation.root</output>
28 <contact>software-tracking@belle2.org</contact>
29 <description>Create momentum resolution, impact parameter resolution and efficiency plots.</description>
36 ROOT.PyConfig.IgnoreCommandLineOptions =
True
37 from ROOT
import TFile, TChain, TH1F, \
38 gStyle, TNamed, TProfile
42 from optparse
import OptionParser
47 PT_VALUES = get_generated_pt_values()
52 CONTACT_PERSON = {
'Name':
'tracking software mailing list',
53 'Email':
'software-tracking@belle2.org'}
56 def make_expert_plot(hist):
57 hist.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'expert'))
61 """Function which is executed"""
63 print(
'Tracking validation plots.')
65 option_parser = OptionParser()
66 option_parser.add_option(
'-i',
'--input-file', dest=
'input_file',
67 default=
'../trackingEfficiency_pt_*.root',
68 help=
'Root file with StandardTrackingPerformance output.'
70 option_parser.add_option(
'-o',
'--output-file', dest=
'output_file',
71 default=
'TrackingValidation.root',
72 help=
'Root file with all histograms.')
74 options = option_parser.parse_args()[0]
80 data_tree = TChain(tree_name)
81 data_tree.Add(options.input_file)
85 number_entries = data_tree.GetEntries()
86 except AttributeError:
87 print(
'Could not load tree with name %s.' % tree_name)
89 if number_entries == 0:
90 print(
'Data tree \'%s\'is empty or does not exist. Exit.' % tree_name)
94 output_file_name = options.output_file
95 output_root_file = TFile(output_file_name,
'recreate')
98 calculate_efficiency_in_pt(data_tree)
100 pt_of_interest = [0.05, 0.25, 1.]
103 for pt_value
in pt_of_interest:
105 generate_cos_theta_plot(data_tree, pt_value)
108 create_momentum_resolution_plot(data_tree)
110 draw_impact_parameter(data_tree)
112 draw_pvalue(data_tree)
114 draw_hit_counts(data_tree, pt_of_interest)
116 draw_residua(data_tree,
'x',
'x_gen', pt_of_interest=pt_of_interest)
117 draw_residua(data_tree,
'y',
'y_gen', pt_of_interest=pt_of_interest)
118 draw_residua(data_tree,
'z',
'z_gen', pt_of_interest=pt_of_interest)
128 pt_of_interest=pt_of_interest,
132 sub_dir =
'additionalPlots'
133 output_root_file.mkdir(sub_dir)
134 output_root_file.cd(sub_dir)
136 for pt_value
in PT_VALUES:
138 generate_cos_theta_plot(data_tree, pt_value)
140 draw_residua(data_tree,
'x',
'x_gen')
141 draw_residua(data_tree,
'y',
'y_gen')
142 draw_residua(data_tree,
'z',
'z_gen')
155 output_root_file.Write()
156 output_root_file.Close()
159 def draw_hit_counts(data_tree, pt_values):
161 Draw the hit count distribution.
167 number_bins = int((pt_upper - pt_lower) / 0.05 + 0.5)
170 for det
in [
'PXD',
'SVD',
'CDC']:
171 hists[det] = TProfile(
'hHitCounts%s' % det,
172 'Hit count profile for the %s;pT;nHits' % det,
173 number_bins, pt_lower, pt_upper)
175 description =
'Distribution of Hit Counts in %s (Contact: %s).' \
176 % (det, CONTACT_PERSON[
'Email'])
179 hists[det].GetListOfFunctions().Add(TNamed(
'Description', description))
180 hists[det].GetListOfFunctions().Add(TNamed(
'Check', check))
181 make_expert_plot(hists[det])
183 hNweights = TH1F(
'hNweights',
'number of weights stored', 201, 0, 201)
184 hWeightsProfile = TProfile(
'hWeightsProfile',
'profile of weights', 201,
186 hWeightsProfile.SetMinimum(0)
187 hWeightsProfile.SetMaximum(1.1)
188 hWeights = TH1F(
'hWeights',
'DAF weights', 50, 0, 1)
190 for track
in data_tree:
191 if not track.pValue == -999:
192 hists[
'PXD'].Fill(track.pt_gen, track.nPXDhits)
193 hists[
'SVD'].Fill(track.pt_gen, track.nSVDhits)
194 hists[
'CDC'].Fill(track.pt_gen, track.nCDChits)
195 hNweights.Fill(track.nWeights)
196 for i
in range(track.nWeights):
197 hWeights.Fill(track.weights[i])
198 hWeightsProfile.Fill(i, track.weights[i])
204 make_expert_plot(hWeightsProfile)
205 hWeightsProfile.Write()
208 def draw_pvalue(data_tree):
210 Create a histogram of the pValue of the track fits
213 number_entries = data_tree.GetEntries()
215 numBins = math.trunc(number_entries / 200)
216 hist_pvalue = TH1F(
'hpValue',
'p-Value Distribution', numBins, 0., 1.)
218 hist_pvalue.SetMinimum(0)
221 for entry
in range(number_entries):
222 data_tree.GetEntry(entry)
225 pvalue = data_tree.GetLeaf(
'pValue').GetValue()
226 except ReferenceError:
227 print(
'The variable "pValue" doesn\'t exist in the tree "%s".\nLeave this function without plotting the variable.'
228 % data_tree.GetName())
234 hist_pvalue.Fill(pvalue)
236 hist_pvalue.SetTitle(
'Distribution of pValue')
237 hist_pvalue.SetXTitle(
'pValue')
238 hist_pvalue.SetYTitle(
'number of entries')
240 description =
'Distribution of pValues of the tracks (Contact: %s).' \
241 % CONTACT_PERSON[
'Email']
242 check =
'Should be a flat distribution.'
244 hist_pvalue.GetListOfFunctions().Add(TNamed(
'Description', description))
245 hist_pvalue.GetListOfFunctions().Add(TNamed(
'Check', check))
246 make_expert_plot(hist_pvalue)
251 def calculate_efficiency_in_pt(data_tree):
252 """Calculate single track reconstruction efficiency in bins of pt"""
257 number_bins = int((pt_upper - pt_lower) / 0.05 + 0.5)
259 hist_pt_gen = TH1F(
'hPtGen',
'hPtGen', number_bins, pt_lower, pt_upper)
260 hist_pt_rec = TH1F(
'hPtRec',
'hPtRec', number_bins, pt_lower, pt_upper)
263 data_tree.Draw(
'pt_gen>>hPtGen',
'',
'goff')
264 data_tree.Draw(
'pt_gen>>hPtRec',
'pt != -999',
'goff')
267 efficiency_hist = TH1F(
'hEfficiency',
'hEfficiency', number_bins,
269 efficiency_hist.SetMinimum(0)
270 efficiency_hist.SetMaximum(1.05)
272 description = (
'Events with 10 muon tracks with fixed transverse '
273 'momentum are generated using the ParticleGun(500 '
274 'events for each pt value). The events are reconstructed '
275 'with VXDTF + CDC Track Finder + VXDCDCMerger.'
276 'plot shows the single track reconstruction efficiency '
277 'over the transverse momentum.')
279 check =
'The efficiency should be stable for higher pt values.'
280 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Description',
282 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Check', check))
283 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Contact',
284 CONTACT_PERSON[
'Email']))
287 for ibin
in range(1, number_bins + 1):
288 number_generated = hist_pt_gen.GetBinContent(ibin)
292 if number_generated > 0:
293 number_reconstructed = hist_pt_rec.GetBinContent(ibin)
294 efficiency = number_reconstructed / number_generated
295 error = math.sqrt(number_reconstructed * (number_generated - number_reconstructed) / pow(number_generated, 3))
297 efficiency_hist.SetBinContent(ibin, efficiency)
298 efficiency_hist.SetBinError(ibin, error)
300 efficiency_hist.SetTitle(
'Tracking efficiency in bins of transverse momentum pt.'
302 efficiency_hist.SetXTitle(
'pt in GeV')
303 efficiency_hist.SetYTitle(
'efficiency')
306 efficiency_hist.Write()
309 def generate_cos_theta_plot(data_tree, pt_value):
310 """Creates a efficiency histo in bins of cos theta"""
316 hist_cos_gen = TH1F(
'hCosGen',
'hCosGen', number_bins, cos_lower,
318 hist_cos_rec = TH1F(
'hCosRec',
'hCosRec', number_bins, cos_lower,
321 data_tree.Draw(
'cosTheta_gen>>hCosGen',
322 'pt_gen>(%.2f - %f) &&pt_gen<(%.2f + %f)' % (pt_value,
323 DELTA_PT, pt_value, DELTA_PT),
'goff')
324 data_tree.Draw(
'cosTheta_gen>>hCosRec',
325 'pt_gen>(%.2f - %f) &&pt_gen<(%.2f + %f) && pt != -999'
326 % (pt_value, DELTA_PT, pt_value, DELTA_PT),
'goff')
328 description = (
'Events with 10 muon tracks with fixed transverse '
329 'momentum are generated using the ParticleGun(500 '
330 'events for each pt value). The events are reconstructed '
331 'with VXDTF + CDCTF + MCTrackCandCombiner. The plot '
332 'shows the single track reconstruction efficiency in '
333 'bins of the polar angle for the fixed transverse '
334 'momentum pt = %.2f GeV.')
335 check =
'Stable efficiency over the hole range of the polar angle.'
337 efficiency_hist = TH1F(
'hEfficiencyPt%.2fGeV' % pt_value,
338 'hEfficiencyPt%.2fGeV' % pt_value, number_bins,
339 cos_lower, cos_upper)
340 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Description',
342 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Check', check))
343 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Contact',
344 CONTACT_PERSON[
'Email']))
346 for ibin
in range(1, number_bins + 1):
349 number_generated = hist_cos_gen.GetBinContent(ibin)
351 if number_generated > 0:
352 number_reconstructed = hist_cos_rec.GetBinContent(ibin)
353 efficiency = number_reconstructed / number_generated
354 error = math.sqrt(number_reconstructed * (number_generated - number_reconstructed) / pow(number_generated, 3))
356 efficiency_hist.SetBinContent(ibin, efficiency)
357 efficiency_hist.SetBinError(ibin, error)
359 efficiency_hist.SetTitle(
'Tracks with pt = %.2f GeV' % pt_value)
360 efficiency_hist.SetXTitle(
'cos #Theta')
361 efficiency_hist.SetYTitle(
'efficiency')
362 make_expert_plot(efficiency_hist)
363 efficiency_hist.Write()
366 def create_momentum_resolution_plot(data_tree):
367 """Create momentum resolution plot"""
372 number_bins = int((pt_upper - pt_lower) / 0.05 + 0.5)
374 sigma_pt_values = calculate_momentum_resolution2(data_tree)
376 hist_resolution = TH1F(
'hPtResolution',
'hPtResolution', number_bins,
380 fit_pt_res_values = []
381 fit_pt_res_errors = []
383 for (pt_value, sigma_pt_value)
in sigma_pt_values.items():
384 ibin = hist_resolution.FindBin(pt_value)
385 bin_center = hist_resolution.GetBinCenter(ibin)
388 if sigma_pt_value[0] != -999:
389 sigma_pt_over_pt = sigma_pt_value[0] / pt_value
390 sigma_pt_error_over_pt = sigma_pt_value[1] / pt_value
392 fit_pt_res_values.append(sigma_pt_over_pt)
393 fit_pt_res_errors.append(sigma_pt_error_over_pt)
394 fit_pt_values.append(bin_center)
396 hist_resolution.SetBinContent(ibin, sigma_pt_over_pt)
397 hist_resolution.SetBinError(ibin, sigma_pt_error_over_pt)
399 hist_resolution.SetTitle(
'Transverse Momentum resolution')
400 hist_resolution.SetXTitle(
'pt in GeV/c')
401 hist_resolution.SetYTitle(
'#sigma_{pt}/pt')
403 description = (
'Events with 10 muon tracks with fixed transverse '
404 'momentum are generated using the ParticleGun(200 '
405 'events for each pt value). The events are reconstructed '
406 'with VXDTF + CDCTF + MCTrackCandCombiner. The plot '
407 'shows the relative momentum resolution of the '
408 'transverse momentum over transverse momentum.')
411 hist_resolution.GetListOfFunctions().Add(TNamed(
'Description',
413 hist_resolution.GetListOfFunctions().Add(TNamed(
'Check', check))
414 hist_resolution.GetListOfFunctions().Add(TNamed(
'Contact',
415 CONTACT_PERSON[
'Email']))
416 hist_resolution.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'logy'))
417 hist_resolution.Write()
422 def get_scaled_rms_90(values, scale_factor=0.79):
426 param values: list of numbers
427 return scaled RMS90 of the distribution in values
430 (vMin, vMax) = (np.nanpercentile(values, 5), np.nanpercentile(values, 95))
431 final_list = np.array(values)[np.logical_and(values > vMin, values < vMax)]
433 rms_90 = np.sqrt(np.mean(np.square(final_list))) / scale_factor
435 rms_error = rms_90 / (scale_factor * np.sqrt(2 * len(final_list)))
437 return (rms_90, rms_error)
440 def value_in_list(test_value, value_list):
442 Function checks, if a given value is in the list values
445 for value
in value_list:
446 if value - DELTA_PT < test_value < value + DELTA_PT:
452 def calculate_momentum_resolution2(data_tree):
454 Calculate momentum resolution
459 for key
in PT_VALUES:
460 delta_pt_dict[key] = []
462 for entry_index
in range(data_tree.GetEntries()):
464 data_tree.GetEntry(entry_index)
466 pt_gen = data_tree.GetLeaf(
'pt_gen').GetValue()
468 test_object = value_in_list(pt_gen, PT_VALUES)
471 pt = data_tree.GetLeaf(
'pt').GetValue()
474 delta_pt_dict[test_object[1]].append(pt - pt_gen)
476 print(
'pt = %0.2f is not generated and not used as key. Abort!'
481 print(
'********************************')
482 print(
'Momentum resolution:')
483 for key
in sorted(delta_pt_dict):
484 (rms90, rms_error) = get_scaled_rms_90(delta_pt_dict[key])
485 pt_resolutions[key] = [rms90, rms_error]
486 print(
'pt = %0.2f: sigma/pt = %0.4f' % (key, rms90 / key))
488 return pt_resolutions
498 Fit a polynomial to data
500 @param x_value_list list of x values
502 @param y_value_list list of y values
504 @param degree degree of the fitting polynomial
506 @param y_value_errors list of corresponding errors on y x_values
510 import scipy.optimize
512 print(
'Module scipy.optimize is not installed. Fit cannot be done.')
522 x_value_list = np.array(x_value_list)
523 y_value_list = np.array(y_value_list)
524 y_value_errors = np.array(y_value_errors)
526 (par_opt, par_cov) = \
527 scipy.optimize.curve_fit(fit_function_sigma_pt_over_pt, x_value_list,
528 y_value_list, [0.002, 0.003],
529 sigma=y_value_errors)
531 print(par_opt, par_cov)
536 def draw_impact_parameter(data_tree):
538 Create a histogram with the impact parameter resolution
540 @param data_tree ROOT tree with the input data
543 print(
'********************************')
544 print(
'Impact parameter:')
548 for key
in PT_VALUES:
549 impact_param_d[key] = []
550 impact_param_z[key] = []
553 number_of_entries = data_tree.GetEntries()
554 except AttributeError:
555 print(
'ERROR: data_tree in function "draw_impact_parameter" is no TTree.')
558 for index_entry
in range(number_of_entries):
559 data_tree.GetEntry(index_entry)
561 pt_gen = data_tree.GetLeaf(
'pt_gen').GetValue()
563 test_object = value_in_list(pt_gen, PT_VALUES)
564 if test_object[0]
is True:
565 poca_x = data_tree.GetLeaf(
'x').GetValue()
570 poca_y = data_tree.GetLeaf(
'y').GetValue()
571 poca_z = data_tree.GetLeaf(
'z').GetValue()
573 z_gen = data_tree.GetLeaf(
'z_gen').GetValue()
575 px = data_tree.GetLeaf(
'px').GetValue()
576 py = data_tree.GetLeaf(
'py').GetValue()
578 d0 = d0_sign([poca_x, poca_y], [px, py]) * np.hypot(poca_x, poca_y)
580 impact_param_d[test_object[1]].append(d0)
581 impact_param_z[test_object[1]].append(poca_z - z_gen)
586 for key
in sorted(impact_param_d):
587 (d0_resolution, d0_error) = get_scaled_rms_90(impact_param_d[key])
588 d0_resolutions[key] = [d0_resolution, d0_error]
589 (z_resolution, z_error) = get_scaled_rms_90(impact_param_z[key])
590 z_resolutions[key] = [z_resolution, z_error]
591 print(
'pt = %0.2f: sigma_d0 = %0.4f, sigma_z = %0.4f' % (key,
592 d0_resolution, z_resolution))
598 hist_impact_parameter_d0 = TH1F(
'hd0resolution',
'hd0resolution',
599 number_bins, lower_edge, upper_edge)
600 hist_impact_parameter_d0.SetTitle(
'd0 resolution')
601 hist_impact_parameter_d0.SetXTitle(
'pt in GeV/c')
602 hist_impact_parameter_d0.SetYTitle(
'#sigma_{d0} [cm]')
603 hist_impact_parameter_d0.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'logy'))
604 hist_impact_parameter_d0.GetListOfFunctions().Add(TNamed(
'Contact',
605 CONTACT_PERSON[
'Email']))
607 hist_impact_parameter_z = TH1F(
'hzresolution',
'hzresolution',
608 number_bins, lower_edge, upper_edge)
609 hist_impact_parameter_z.SetTitle(
'z resolution')
610 hist_impact_parameter_z.SetXTitle(
'pt in GeV/c')
611 hist_impact_parameter_z.SetYTitle(
'#sigma_{z} [cm]')
612 hist_impact_parameter_z.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'logy'))
613 hist_impact_parameter_z.GetListOfFunctions().Add(TNamed(
'Contact',
614 CONTACT_PERSON[
'Email']))
617 ibin = hist_impact_parameter_d0.FindBin(pt)
618 if d0_resolutions[pt][0] > 0
and d0_resolutions[pt][1] > 0:
619 hist_impact_parameter_d0.SetBinContent(ibin, d0_resolutions[pt][0])
620 hist_impact_parameter_d0.SetBinError(ibin, d0_resolutions[pt][1])
621 if z_resolutions[pt][0] > 0
and z_resolutions[pt][1] > 0:
622 hist_impact_parameter_z.SetBinContent(ibin, z_resolutions[pt][0])
623 hist_impact_parameter_z.SetBinError(ibin, z_resolutions[pt][1])
625 hist_impact_parameter_d0.Write()
626 hist_impact_parameter_z.Write()
629 def scale_histogram(hist):
631 Scales a TH1F using the TH1::Scale function
634 if isinstance(hist, TH1F):
635 if hist.GetEntries() > 0:
636 hist.Scale(1 / hist.GetEntries())
638 print(
'Cannot scale. No entries in histogram ' + hist.GetName())
640 print(
'Not a TH1F. Not able to scale.')
643 def d0_sign(vector_d, momentum):
645 Estimate the sign of the impact parameter d0_sign
650 if len(vector_d) != 2
or len(momentum) != 2:
651 print(
'Need x and y values of d and p_d.')
654 phi_momentum = np.arctan2(momentum[1], momentum[0])
655 phi_d = np.arctan2(vector_d[1], vector_d[0])
657 diff = phi_momentum - phi_d
660 diff = diff - 2 * np.pi
662 diff = 2 * np.pi + diff
678 Write histogram of difference of variable_name (reconstructed) and
679 gen_variable_name (generated) in gDirectory
682 if pt_of_interest
is None:
685 used_pts = [pt
for pt
in pt_of_interest
if pt
in PT_VALUES]
687 number_entries = data_tree.GetEntries()
690 for pt_value
in used_pts:
691 histograms[pt_value] = TH1F(
'h%sResiduum_%0.2fGeV' % (variable_name,
692 pt_value),
'h%sResiduum_%0.2fGeV'
693 % (variable_name, pt_value), bins, ledge,
696 for index
in range(number_entries):
697 data_tree.GetEntry(index)
699 variable_reconstructed = data_tree.GetLeaf(variable_name).GetValue()
700 if variable_reconstructed != -999:
701 variable_generated = \
702 data_tree.GetLeaf(gen_variable_name).GetValue()
703 pt_gen = round(data_tree.GetLeaf(
'pt_gen').GetValue(), 2)
704 if pt_gen
in used_pts:
706 histograms[pt_gen].Fill((variable_reconstructed - variable_generated) / variable_generated)
708 histograms[pt_gen].Fill(variable_reconstructed - variable_generated)
710 for (pt, hist)
in histograms.items():
711 scale_histogram(hist)
713 hist.SetXTitle(
'(%s - %s) / %s' % (variable_name,
714 gen_variable_name, gen_variable_name))
716 hist.SetXTitle(
'(%s - %s)' % (variable_name, gen_variable_name))
717 hist.GetListOfFunctions().Add(TNamed(
'Contact', CONTACT_PERSON[
'Email'
719 make_expert_plot(hist)
723 def tf1_fit_function(x, par):
724 return fit_function_sigma_pt_over_pt(x, par[0], par[1])
727 def fit_function_sigma_pt_over_pt(x, a, b):
728 return np.sqrt((a * x) ** 2 + b * b)
731 if __name__ ==
'__main__':
int main(int argc, char **argv)
Run all tests.