20 <input>trackingEfficiency_pt_0.05GeV.root,
21 trackingEfficiency_pt_0.10GeV.root,trackingEfficiency_pt_0.25GeV.root,
22 trackingEfficiency_pt_0.40GeV.root,trackingEfficiency_pt_0.60GeV.root,
23 trackingEfficiency_pt_1.00GeV.root,trackingEfficiency_pt_1.50GeV.root,
24 trackingEfficiency_pt_2.00GeV.root,trackingEfficiency_pt_3.00GeV.root,
25 trackingEfficiency_pt_4.00GeV.root</input>
26 <output>TrackingValidation.root</output>
27 <contact>software-tracking@belle2.org</contact>
28 <description>Create momentum resolution, impact parameter resolution
and efficiency plots.</description>
35ROOT.PyConfig.IgnoreCommandLineOptions = True
36from ROOT
import TFile, TChain, TH1F, \
37 gStyle, TNamed, TProfile
41from optparse
import OptionParser
48PT_VALUES = get_generated_pt_values()
53CONTACT_PERSON = {
'Name':
'tracking software mailing list',
54 'Email':
'software-tracking@belle2.org'}
57def make_expert_plot(hist):
58 hist.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'expert'))
62 """Function which is executed"""
64 print(
'Tracking validation plots.')
66 option_parser = OptionParser()
67 option_parser.add_option(
'-i',
'--input-file', dest=
'input_file',
68 default=
'../trackingEfficiency_pt_*.root',
69 help=
'Root file with StandardTrackingPerformance output.'
71 option_parser.add_option(
'-o',
'--output-file', dest=
'output_file',
72 default=
'TrackingValidation.root',
73 help=
'Root file with all histograms.')
75 options = option_parser.parse_args()[0]
81 data_tree = TChain(tree_name)
82 data_tree.Add(options.input_file)
86 number_entries = data_tree.GetEntries()
87 except AttributeError:
88 print(f
'Could not load tree with name {tree_name}.')
90 if number_entries == 0:
91 print(f
'Data tree \'{tree_name}\'is empty or does not exist. Exit.')
95 output_file_name = options.output_file
96 output_root_file = TFile(output_file_name,
'recreate')
99 calculate_efficiency_in_pt(data_tree)
101 pt_of_interest = [0.05, 0.25, 1.]
104 for pt_value
in pt_of_interest:
106 generate_cos_theta_plot(data_tree, pt_value)
109 create_momentum_resolution_plot(data_tree)
111 draw_impact_parameter(data_tree)
113 draw_pvalue(data_tree)
115 draw_hit_counts(data_tree, pt_of_interest)
117 draw_residua(data_tree,
'x',
'x_gen', pt_of_interest=pt_of_interest)
118 draw_residua(data_tree,
'y',
'y_gen', pt_of_interest=pt_of_interest)
119 draw_residua(data_tree,
'z',
'z_gen', pt_of_interest=pt_of_interest)
129 pt_of_interest=pt_of_interest,
133 sub_dir =
'additionalPlots'
134 output_root_file.mkdir(sub_dir)
135 output_root_file.cd(sub_dir)
137 for pt_value
in PT_VALUES:
139 generate_cos_theta_plot(data_tree, pt_value)
141 draw_residua(data_tree,
'x',
'x_gen')
142 draw_residua(data_tree,
'y',
'y_gen')
143 draw_residua(data_tree,
'z',
'z_gen')
156 output_root_file.Write()
157 output_root_file.Close()
160def draw_hit_counts(data_tree, pt_values):
162 Draw the hit count distribution.
168 number_bins = int((pt_upper - pt_lower) / 0.05 + 0.5)
171 for det
in [
'PXD',
'SVD',
'CDC']:
172 hists[det] = TProfile(f
'hHitCounts{det}',
173 f
'Hit count profile for the {det};pT;nHits',
174 number_bins, pt_lower, pt_upper)
176 description = f
'Distribution of Hit Counts in {det} (Contact: {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()
208def 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(f
'The variable "pValue" doesn\'t exist in the tree "{data_tree.GetName()}".\n'
228 'Leave this function without plotting the variable.')
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 = f
'Distribution of pValues of the tracks (Contact: {CONTACT_PERSON["Email"]}).'
241 check =
'Should be a flat distribution.'
243 hist_pvalue.GetListOfFunctions().Add(TNamed(
'Description', description))
244 hist_pvalue.GetListOfFunctions().Add(TNamed(
'Check', check))
245 make_expert_plot(hist_pvalue)
250def calculate_efficiency_in_pt(data_tree):
251 """Calculate single track reconstruction efficiency in bins of pt"""
256 number_bins = int((pt_upper - pt_lower) / 0.05 + 0.5)
258 hist_pt_gen = TH1F(
'hPtGen',
'hPtGen', number_bins, pt_lower, pt_upper)
259 hist_pt_rec = TH1F(
'hPtRec',
'hPtRec', number_bins, pt_lower, pt_upper)
262 data_tree.Draw(
'pt_gen>>hPtGen',
'',
'goff')
263 data_tree.Draw(
'pt_gen>>hPtRec',
'pt != -999',
'goff')
266 efficiency_hist = TH1F(
'hEfficiency',
'hEfficiency', number_bins,
268 efficiency_hist.SetMinimum(0)
269 efficiency_hist.SetMaximum(1.05)
271 description = (
'Events with 10 muon tracks with fixed transverse '
272 'momentum are generated using the ParticleGun(500 '
273 'events for each pt value). The events are reconstructed '
274 'with VXDTF + CDC Track Finder + VXDCDCMerger.'
275 'plot shows the single track reconstruction efficiency '
276 'over the transverse momentum.')
278 check =
'The efficiency should be stable for higher pt values.'
279 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Description',
281 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Check', check))
282 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Contact',
283 CONTACT_PERSON[
'Email']))
286 for ibin
in range(1, number_bins + 1):
287 number_generated = hist_pt_gen.GetBinContent(ibin)
291 if number_generated > 0:
292 number_reconstructed = hist_pt_rec.GetBinContent(ibin)
293 efficiency = number_reconstructed / number_generated
294 error = math.sqrt(number_reconstructed * (number_generated - number_reconstructed) / pow(number_generated, 3))
296 efficiency_hist.SetBinContent(ibin, efficiency)
297 efficiency_hist.SetBinError(ibin, error)
299 efficiency_hist.SetTitle(
'Tracking efficiency in bins of transverse momentum pt.'
301 efficiency_hist.SetXTitle(
'pt in GeV')
302 efficiency_hist.SetYTitle(
'efficiency')
305 efficiency_hist.Write()
308def generate_cos_theta_plot(data_tree, pt_value):
309 """Creates a efficiency histo in bins of cos theta"""
315 hist_cos_gen = TH1F(
'hCosGen',
'hCosGen', number_bins, cos_lower,
317 hist_cos_rec = TH1F(
'hCosRec',
'hCosRec', number_bins, cos_lower,
320 data_tree.Draw(
'cosTheta_gen>>hCosGen',
321 f
'pt_gen>({pt_value:.2f} - {DELTA_PT:f}) && pt_gen<({pt_value:.2f} + {DELTA_PT:f})',
'goff')
322 data_tree.Draw(
'cosTheta_gen>>hCosRec',
323 f
'pt_gen>({pt_value:.2f} - {DELTA_PT:f}) && pt_gen<({pt_value:.2f} + {DELTA_PT:f}) && pt != -999',
'goff')
325 description = (
'Events with 10 muon tracks with fixed transverse '
326 'momentum are generated using the ParticleGun(500 '
327 'events for each pt value). The events are reconstructed '
328 'with VXDTF + CDCTF + MCTrackCandCombiner. The plot '
329 'shows the single track reconstruction efficiency in '
330 'bins of the polar angle for the fixed transverse '
331 'momentum pt = %.2f GeV.')
332 check =
'Stable efficiency over the hole range of the polar angle.'
334 efficiency_hist = TH1F(f
'hEfficiencyPt{pt_value:.2f}GeV',
335 f
'hEfficiencyPt{pt_value:.2f}GeV',
336 number_bins, cos_lower, cos_upper)
337 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Description',
339 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Check', check))
340 efficiency_hist.GetListOfFunctions().Add(TNamed(
'Contact',
341 CONTACT_PERSON[
'Email']))
343 for ibin
in range(1, number_bins + 1):
346 number_generated = hist_cos_gen.GetBinContent(ibin)
348 if number_generated > 0:
349 number_reconstructed = hist_cos_rec.GetBinContent(ibin)
350 efficiency = number_reconstructed / number_generated
351 error = math.sqrt(number_reconstructed * (number_generated - number_reconstructed) / pow(number_generated, 3))
353 efficiency_hist.SetBinContent(ibin, efficiency)
354 efficiency_hist.SetBinError(ibin, error)
356 efficiency_hist.SetTitle(f
'Tracks with pt = {pt_value:.2f} GeV')
357 efficiency_hist.SetXTitle(
'cos #Theta')
358 efficiency_hist.SetYTitle(
'efficiency')
359 make_expert_plot(efficiency_hist)
360 efficiency_hist.Write()
363def create_momentum_resolution_plot(data_tree):
364 """Create momentum resolution plot"""
369 number_bins = int((pt_upper - pt_lower) / 0.05 + 0.5)
371 sigma_pt_values = calculate_momentum_resolution2(data_tree)
373 hist_resolution = TH1F(
'hPtResolution',
'hPtResolution', number_bins,
377 fit_pt_res_values = []
378 fit_pt_res_errors = []
380 for (pt_value, sigma_pt_value)
in sigma_pt_values.items():
381 ibin = hist_resolution.FindBin(pt_value)
382 bin_center = hist_resolution.GetBinCenter(ibin)
385 if sigma_pt_value[0] != -999:
386 sigma_pt_over_pt = sigma_pt_value[0] / pt_value
387 sigma_pt_error_over_pt = sigma_pt_value[1] / pt_value
389 fit_pt_res_values.append(sigma_pt_over_pt)
390 fit_pt_res_errors.append(sigma_pt_error_over_pt)
391 fit_pt_values.append(bin_center)
393 hist_resolution.SetBinContent(ibin, sigma_pt_over_pt)
394 hist_resolution.SetBinError(ibin, sigma_pt_error_over_pt)
396 hist_resolution.SetTitle(
'Transverse Momentum resolution')
397 hist_resolution.SetXTitle(
'pt in GeV/c')
398 hist_resolution.SetYTitle(
'#sigma_{pt}/pt')
400 description = (
'Events with 10 muon tracks with fixed transverse '
401 'momentum are generated using the ParticleGun(200 '
402 'events for each pt value). The events are reconstructed '
403 'with VXDTF + CDCTF + MCTrackCandCombiner. The plot '
404 'shows the relative momentum resolution of the '
405 'transverse momentum over transverse momentum.')
408 hist_resolution.GetListOfFunctions().Add(TNamed(
'Description',
410 hist_resolution.GetListOfFunctions().Add(TNamed(
'Check', check))
411 hist_resolution.GetListOfFunctions().Add(TNamed(
'Contact',
412 CONTACT_PERSON[
'Email']))
413 hist_resolution.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'logy'))
414 hist_resolution.Write()
419def get_scaled_rms_90(values, scale_factor=0.79):
423 param values: list of numbers
424 return scaled RMS90 of the distribution
in values
427 (vMin, vMax) = (np.nanpercentile(values, 5), np.nanpercentile(values, 95))
428 final_list = np.array(values)[np.logical_and(values > vMin, values < vMax)]
430 rms_90 = np.sqrt(np.mean(np.square(final_list))) / scale_factor
432 rms_error = rms_90 / (scale_factor * np.sqrt(2 * len(final_list)))
434 return (rms_90, rms_error)
437def value_in_list(test_value, value_list):
439 Function checks, if a given value
is in the list values
442 for value
in value_list:
443 if value - DELTA_PT < test_value < value + DELTA_PT:
449def calculate_momentum_resolution2(data_tree):
451 Calculate momentum resolution
456 for key
in PT_VALUES:
457 delta_pt_dict[key] = []
459 for entry_index
in range(data_tree.GetEntries()):
461 data_tree.GetEntry(entry_index)
463 pt_gen = data_tree.GetLeaf(
'pt_gen').GetValue()
465 test_object = value_in_list(pt_gen, PT_VALUES)
468 pt = data_tree.GetLeaf(
'pt').GetValue()
471 delta_pt_dict[test_object[1]].append(pt - pt_gen)
473 print(f
'pt = {test_object[1]:0.2f} is not generated and not used as key. Abort!')
477 print(
'********************************')
478 print(
'Momentum resolution:')
479 for key
in sorted(delta_pt_dict):
480 (rms90, rms_error) = get_scaled_rms_90(delta_pt_dict[key])
481 pt_resolutions[key] = [rms90, rms_error]
482 print(f
'pt = {key:0.2f}: sigma/pt = {rms90/key:0.4f}')
484 return pt_resolutions
494 Fit a polynomial to data
496 @param x_value_list list of x values
498 @param y_value_list list of y values
500 @param degree degree of the fitting polynomial
502 @param y_value_errors list of corresponding errors on y x_values
506 import scipy.optimize
508 print(
'Module scipy.optimize is not installed. Fit cannot be done.')
518 x_value_list = np.array(x_value_list)
519 y_value_list = np.array(y_value_list)
520 y_value_errors = np.array(y_value_errors)
522 (par_opt, par_cov) = \
523 scipy.optimize.curve_fit(fit_function_sigma_pt_over_pt, x_value_list,
524 y_value_list, [0.002, 0.003],
525 sigma=y_value_errors)
527 print(par_opt, par_cov)
532def draw_impact_parameter(data_tree):
534 Create a histogram with the impact parameter resolution
536 @param data_tree ROOT tree
with the input data
539 print('********************************')
540 print(
'Impact parameter:')
544 for key
in PT_VALUES:
545 impact_param_d[key] = []
546 impact_param_z[key] = []
549 number_of_entries = data_tree.GetEntries()
550 except AttributeError:
551 print(
'ERROR: data_tree in function "draw_impact_parameter" is no TTree.')
554 for index_entry
in range(number_of_entries):
555 data_tree.GetEntry(index_entry)
557 pt_gen = data_tree.GetLeaf(
'pt_gen').GetValue()
559 test_object = value_in_list(pt_gen, PT_VALUES)
560 if test_object[0]
is True:
561 poca_x = data_tree.GetLeaf(
'x').GetValue()
566 poca_y = data_tree.GetLeaf(
'y').GetValue()
567 poca_z = data_tree.GetLeaf(
'z').GetValue()
569 z_gen = data_tree.GetLeaf(
'z_gen').GetValue()
571 px = data_tree.GetLeaf(
'px').GetValue()
572 py = data_tree.GetLeaf(
'py').GetValue()
574 d0 = d0_sign([poca_x, poca_y], [px, py]) * np.hypot(poca_x, poca_y)
576 impact_param_d[test_object[1]].append(d0)
577 impact_param_z[test_object[1]].append(poca_z - z_gen)
582 for key
in sorted(impact_param_d):
583 (d0_resolution, d0_error) = get_scaled_rms_90(impact_param_d[key])
584 d0_resolutions[key] = [d0_resolution, d0_error]
585 (z_resolution, z_error) = get_scaled_rms_90(impact_param_z[key])
586 z_resolutions[key] = [z_resolution, z_error]
587 print(f
'pt = {key:0.2f}: sigma_d0 = {d0_resolution:0.4f}, sigma_z = {z_resolution:0.4f}')
593 hist_impact_parameter_d0 = TH1F(
'hd0resolution',
'hd0resolution',
594 number_bins, lower_edge, upper_edge)
595 hist_impact_parameter_d0.SetTitle(
'd0 resolution')
596 hist_impact_parameter_d0.SetXTitle(
'pt in GeV/c')
597 hist_impact_parameter_d0.SetYTitle(
'#sigma_{d0} [cm]')
598 hist_impact_parameter_d0.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'logy'))
599 hist_impact_parameter_d0.GetListOfFunctions().Add(TNamed(
'Contact',
600 CONTACT_PERSON[
'Email']))
602 hist_impact_parameter_z = TH1F(
'hzresolution',
'hzresolution',
603 number_bins, lower_edge, upper_edge)
604 hist_impact_parameter_z.SetTitle(
'z resolution')
605 hist_impact_parameter_z.SetXTitle(
'pt in GeV/c')
606 hist_impact_parameter_z.SetYTitle(
'#sigma_{z} [cm]')
607 hist_impact_parameter_z.GetListOfFunctions().Add(TNamed(
'MetaOptions',
'logy'))
608 hist_impact_parameter_z.GetListOfFunctions().Add(TNamed(
'Contact',
609 CONTACT_PERSON[
'Email']))
612 ibin = hist_impact_parameter_d0.FindBin(pt)
613 if d0_resolutions[pt][0] > 0
and d0_resolutions[pt][1] > 0:
614 hist_impact_parameter_d0.SetBinContent(ibin, d0_resolutions[pt][0])
615 hist_impact_parameter_d0.SetBinError(ibin, d0_resolutions[pt][1])
616 if z_resolutions[pt][0] > 0
and z_resolutions[pt][1] > 0:
617 hist_impact_parameter_z.SetBinContent(ibin, z_resolutions[pt][0])
618 hist_impact_parameter_z.SetBinError(ibin, z_resolutions[pt][1])
620 hist_impact_parameter_d0.Write()
621 hist_impact_parameter_z.Write()
624def scale_histogram(hist):
626 Scales a TH1F using the TH1::Scale function
629 if isinstance(hist, TH1F):
630 if hist.GetEntries() > 0:
631 hist.Scale(1 / hist.GetEntries())
633 print(
'Cannot scale. No entries in histogram ' + hist.GetName())
635 print(
'Not a TH1F. Not able to scale.')
638def d0_sign(vector_d, momentum):
640 Estimate the sign of the impact parameter d0_sign
645 if len(vector_d) != 2
or len(momentum) != 2:
646 print(
'Need x and y values of d and p_d.')
649 phi_momentum = np.arctan2(momentum[1], momentum[0])
650 phi_d = np.arctan2(vector_d[1], vector_d[0])
652 diff = phi_momentum - phi_d
655 diff = diff - 2 * np.pi
657 diff = 2 * np.pi + diff
673 Write histogram of difference of variable_name (reconstructed) and
674 gen_variable_name (generated)
in gDirectory
677 if pt_of_interest
is None:
680 used_pts = [pt
for pt
in pt_of_interest
if pt
in PT_VALUES]
682 number_entries = data_tree.GetEntries()
685 for pt_value
in used_pts:
686 histograms[pt_value] = TH1F(f
'h{variable_name}Residuum_{pt_value:0.2f}GeV',
687 f
'h{variable_name}Residuum_{pt_value:0.2f}GeV',
690 for index
in range(number_entries):
691 data_tree.GetEntry(index)
693 variable_reconstructed = data_tree.GetLeaf(variable_name).GetValue()
694 if variable_reconstructed != -999:
695 variable_generated = \
696 data_tree.GetLeaf(gen_variable_name).GetValue()
697 pt_gen = round(data_tree.GetLeaf(
'pt_gen').GetValue(), 2)
698 if pt_gen
in used_pts:
700 histograms[pt_gen].Fill((variable_reconstructed - variable_generated) / variable_generated)
702 histograms[pt_gen].Fill(variable_reconstructed - variable_generated)
704 for (pt, hist)
in histograms.items():
705 scale_histogram(hist)
707 hist.SetXTitle(f
'({variable_name} - {gen_variable_name}) / {gen_variable_name}')
709 hist.SetXTitle(f
'({variable_name} - {gen_variable_name})')
710 hist.GetListOfFunctions().Add(TNamed(
'Contact', CONTACT_PERSON[
'Email'
712 make_expert_plot(hist)
716def tf1_fit_function(x, par):
717 return fit_function_sigma_pt_over_pt(x, par[0], par[1])
720def fit_function_sigma_pt_over_pt(x, a, b):
721 return np.sqrt((a * x) ** 2 + b * b)
724if __name__ ==
'__main__':
728 print(
"This validation deactivated and thus basf2 is not executed.\n"
729 "If you want to run this validation, please set the 'ACTIVE' flag above to 'True'.\n"