Belle II Software development
segmentPairCreationValidation.py
1#!/usr/bin/env python3
2
3
10
11import logging
12from tracking.run.mixins import BrowseTFileOnTerminateRunMixin
13from tracking.run.event_generation import StandardEventGenerationRun
14import tracking.metamodules as metamodules
15import tracking.harvest.refiners as refiners
16import tracking.harvest.harvesting as harvesting
17from tracking.validation.utilities import prob, is_primary
18from ROOT import Belle2 # make Belle2 namespace available
19import sys
20import numpy as np
21
22import basf2
23
24from ROOT import gSystem
25gSystem.Load('libtracking')
26gSystem.Load('libtrackFindingCDC')
27
28
29def get_logger():
30 return logging.getLogger(__name__)
31
32
34 """Generate, postprocess and inspect MC events for track segment-pair validation"""
35
36 segment_finder_module = basf2.register_module("TFCDC_SegmentCreatorMCTruth")
37 segment_finder_module.param({"MinCDCHits": 4})
38
39
40 segment_pair_finder_module = basf2.register_module("TFCDC_TrackFinderSegmentPairAutomaton")
41 segment_pair_finder_module.param({
42 "WriteSegmentPairs": True,
43 "SegmentPairFilter": "all",
44 "SegmentPairRelationFilter": "none",
45 })
46
47
48 py_profile = True
49
50 output_file_name = "SegmentPairCreationValidation.root" # Specification for BrowseTFileOnTerminateRunMixin
51
52 def create_argument_parser(self, **kwds):
53 """Convert command-line arguments to basf2 argument list"""
54 argument_parser = super().create_argument_parser(**kwds)
55 return argument_parser
56
57 def create_path(self):
58 """
59 Sets up a path that plays back pregenerated events or generates events
60 based on the properties in the base class.
61 """
62 main_path = super().create_path()
63
64 segment_finder_module = self.get_basf2_module(self.segment_finder_module)
65 main_path.add_module(segment_finder_module)
66
67 main_path.add_module("TFCDC_SegmentFitter")
68
69 segment_pair_finder_module = self.get_basf2_module(self.segment_pair_finder_module)
70 main_path.add_module(segment_pair_finder_module)
71
72 # main_path.add_module(AxialStereoPairFitterModule())
73 validation_module = SegmentPairCreationValidationModule(output_file_name=self.output_file_nameoutput_file_name)
74 if self.py_profile:
75 main_path.add_module(metamodules.PyProfilingModule(validation_module))
76 else:
77 main_path.add_module(validation_module)
78
79 return main_path
80
81
82class SegmentPairCreationValidationModule(harvesting.HarvestingModule):
83
84 """Module to collect information about the generated segments and compose validation plots on terminate."""
85
86 def __init__(self, output_file_name):
87 """Constructor"""
88 super().__init__(foreach="CDCSegmentPairVector",
89 output_file_name=output_file_name)
90
92
94
96
97 def initialize(self):
98 """Receive signal at the start of event processing"""
99 super().initialize()
103
104 def prepare(self):
105 """Initialize the MC-hit lookup method"""
107
108 def pick(self, segment_pair_relation):
109 """Select segment pairs with 4 or more hit in each segments and a matching primary MC particle"""
110 mc_segment_lookup = self.mc_segment_lookup
111 start_segment = segment_pair_relation.getStartSegment()
112 end_segment = segment_pair_relation.getEndSegment()
113 mc_particle = mc_segment_lookup.getMCParticle(start_segment)
114 return (mc_particle and
115 is_primary(mc_particle) and
116 start_segment.size() > 3 and
117 end_segment.size() > 3)
118
119 def peel(self, segment_pair_relation):
120 """Aggregate the track and MC information for track segment-pair analysis"""
121 crops = self.peel_target(segment_pair_relation)
122 crops.update(self.peel_mc(segment_pair_relation))
123 crops.update(self.peel_fit(segment_pair_relation))
124 return crops
125
126 def peel_target(self, segment_pair_relation):
127 """Create a dictionary of MC-truth (weight,decision) pairs"""
128 mc_weight = self.mc_segment_pair_filter(segment_pair_relation)
129 mc_decision = np.isfinite(mc_weight) # Filters for nan
130
131 return dict(
132 mc_weight=mc_weight,
133 mc_decision=mc_decision,
134 )
135
136 def peel_mc(self, segment_pair_relation):
137 """Create a dictionary of MC-truth (curvature,tanlambda) pairs"""
138 mc_segment_lookup = self.mc_segment_lookup
139
140 end_segment = segment_pair_relation.getEndSegment()
141
142 # Take the fit best at the middle of the segment pair
143 # mc_particle = mc_segment_lookup.getMCParticle(end_segment)
144 fit3d_truth = mc_segment_lookup.getTrajectory3D(end_segment)
145
146 return dict(
147 curvature_truth=fit3d_truth.getCurvatureXY(),
148 tan_lambda_truth=fit3d_truth.getTanLambda(),
149 )
150
151 def peel_fit(self, segment_pair_relation):
152 """Create a dictionary of track-segment-fit information"""
153 fitless_crops = self.peel_fitless(segment_pair_relation)
154
155 select_fitless = fitless_crops["select_fitless"]
156 if select_fitless:
157 # Now fit
158 self.fit(segment_pair_relation)
159 fit3d = segment_pair_relation.getTrajectory3D()
160
161 i_curv = 0
162 i_tan_lambda = 3
163
164 chi2 = fit3d.getChi2()
165 ndf = fit3d.getNDF()
166
167 curvature_estimate = fit3d.getCurvatureXY()
168 curvature_variance = fit3d.getLocalVariance(i_curv)
169
170 tan_lambda_estimate = fit3d.getTanLambda()
171 tan_lambda_variance = fit3d.getLocalVariance(i_tan_lambda)
172
173 chi2 = chi2
174 ndf = ndf
175 p_value = prob(chi2, ndf)
176 # select = True
177
178 else:
179 nan = float('nan')
180 curvature_estimate = nan
181 curvature_variance = nan
182
183 tan_lambda_estimate = nan
184 tan_lambda_variance = nan
185
186 chi2 = nan
187 ndf = nan
188 p_value = nan
189
190 crops = dict(
191 curvature_estimate=curvature_estimate,
192 curvature_variance=curvature_variance,
193
194 tan_lambda_estimate=tan_lambda_estimate,
195 tan_lambda_variance=tan_lambda_variance,
196
197 chi2=chi2,
198 ndf=ndf,
199 p_value=p_value,
200 )
201
202 if select_fitless:
203 crops["select"] = self.select(crops)
204 else:
205 crops["select"] = False
206
207 crops.update(fitless_crops)
208
209 return crops
210
211 def peel_fitless(self, segment_pair_relation):
212 """Create a dictionary of track-segments-without-fit information"""
213 # Try to make some judgements without executing the common fit.
214
215 start_segment = segment_pair_relation.getStartSegment()
216 end_segment = segment_pair_relation.getEndSegment()
217
218 start_fit2d = start_segment.getTrajectory2D()
219 end_fit2d = end_segment.getTrajectory2D()
220
221 start_superlayer_id = start_segment.getISuperLayer()
222 end_superlayer_id = end_segment.getISuperLayer()
223
224 sorted_superlayer_ids = sorted([start_superlayer_id, end_superlayer_id])
225
226 superlayer_id_pair = 10.0 * sorted_superlayer_ids[1] + sorted_superlayer_ids[0]
227
228 fitless_crops = dict(
229 start_superlayer_id=start_superlayer_id,
230 end_superlayer_id=end_superlayer_id,
231 superlayer_id_pair=superlayer_id_pair,
232
233 start_size=start_segment.size(),
234 end_size=end_segment.size(),
235
236 start_curvature_estimate=start_fit2d.getCurvature(),
237 end_curvature_estimate=end_fit2d.getCurvature(),
238
239 delta_phi=segment_pair_relation.computeDeltaPhiAtSuperLayerBound(),
240 is_coaligned=segment_pair_relation.computeIsCoaligned(),
241
242 start_is_before_end=segment_pair_relation.computeStartIsBeforeEnd(),
243 end_is_after_start=segment_pair_relation.computeEndIsAfterStart(),
244 )
245
246 fitless_crops["select_fitless"] = self.select_fitless(fitless_crops)
247 return fitless_crops
248
249 def fit(self, segment_pair_relation):
250 """Fit the segment pair"""
251 self.segment_pair_fusion.reconstructFuseTrajectories(segment_pair_relation, True)
252
253
254 delta_phi_cut_value = 1.0
255
256 is_after_cut_value = 1.0
257
258 def select_fitless(self, fitless_crops):
259 """Selection of track-segments-without-fit"""
260 delta_phi = fitless_crops["delta_phi"]
261 start_is_before_end = fitless_crops["start_is_before_end"]
262 end_is_after_start = fitless_crops["end_is_after_start"]
263 is_after_select = (abs(start_is_before_end) < self.is_after_cut_value) & (abs(end_is_after_start) < self.is_after_cut_value)
264 return (abs(delta_phi) < self.delta_phi_cut_value) & is_after_select
265
266 def select(self, crops):
267 """Select every track-segment-pair"""
268 return True
269
270 # Refiners to be executed at the end of the harvesting / termination of the module
271
272 save_histograms = refiners.save_histograms(outlier_z_score=5.0, allow_discrete=True)
273
274 save_tree = refiners.save_tree()
275
276 # Investigate the preselection
277
278 save_fitless_selection_variables_histograms = refiners.save_histograms(
279 select=["mc_decision", "delta_phi", "start_is_before_end", "end_is_after_start", "is_coaligned"],
280 outlier_z_score=5.0,
281 allow_discrete=True,
282 stackby="mc_decision",
283 folder_name="fitless_selection_variables",
284 )
285
286
287 save_view_is_after_cut_histograms = refiners.save_histograms(
288 select=["mc_decision", "start_is_before_end", "end_is_after_start"],
289 lower_bound=-is_after_cut_value,
290 upper_bound=is_after_cut_value,
291 stackby="mc_decision",
292 folder_name="view_fitless_cuts",
293 )
294
295
296 save_view_delta_phi_cut_histograms = refiners.save_histograms(
297 select=["mc_decision", "delta_phi"],
298 lower_bound=-delta_phi_cut_value,
299 upper_bound=delta_phi_cut_value,
300 stackby="mc_decision",
301 folder_name="view_fitless_cuts",
302 )
303
304 # Investigate the main selection
305
306 save_selection_variables_after_fitless_selection_histograms = refiners.save_histograms(
307 select=["mc_decision", "chi2", "ndf", "p_value"],
308 outlier_z_score=5.0,
309 allow_discrete=True,
310 stackby="mc_decision",
311 folder_name="selection_variables_after_fitless_selection",
312 filter_on="select_fitless",
313 )
314
315 # TODO: Is this interesting enough to keep it.
316
317 save_p_value_over_curvature_profile = refiners.save_profiles(
318 select={"p_value": "p-value", "curvature_truth": "true curvature"},
319 y="p-value",
320 folder_name="selection_variables_after_fitless_selection",
321 title=r"$p$-value versus true curvature after fitless selection",
322 filter_on="select_fitless",
323 )
324
325 # ! @cond Doxygen_Suppress
326 @refiners.context(groupby=[None, "superlayer_id_pair"], exclude_groupby=False)
327 # ! @endcond
328 def print_signal_number(self, crops, tdirectory, **kwds):
329 """Print diagnostic information about the track-segment-pair selection"""
330 info = get_logger().info
331
332 # start_superlayer_ids = crops["start_superlayer_id"]
333 # end_superlayer_ids = crops["end_superlayer_id"]
334
335 superlayer_id_pair = crops["superlayer_id_pair"]
336 info("Number of pairs in superlayers %s : %s", np.unique(superlayer_id_pair), len(superlayer_id_pair))
337
338 mc_decisions = crops["mc_decision"]
339 n = len(mc_decisions)
340 n_signal = np.sum(mc_decisions)
341 n_background = n - n_signal
342 info("#Signal : %s", n_signal)
343 info("#Background : %s", n_background)
344
345 fitless_selections = np.nonzero(crops["select_fitless"])
346 info("#Signal after precut : %s", np.sum(mc_decisions[fitless_selections]))
347 info("#Background after precut : %s", np.sum(1 - mc_decisions[fitless_selections]))
348
349
350def main():
352 run.configure_and_execute_from_commandline()
353
354
355if __name__ == "__main__":
356 logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(levelname)s:%(message)s')
357 main()
Utility class implementing the Kalmanesk combination of to two dimensional trajectories to one three ...
static const CDCMCHitLookUp & getInstance()
Getter for the singletone instance.
static const CDCMCSegment2DLookUp & getInstance()
Getter for the singletone instance.
Filter for the construction of axial to stereo segment pairs based on MC information.
float delta_phi_cut_value
default selection for the delta-phi of the segment pair
mc_segment_pair_filter
defer reference to MCSegmentPairFilter until after it is constructed
segment_pair_fusion
defer reference to CDCAxialStereoFusion until after it is constructed
mc_segment_lookup
defer reference to CDCMCSegment2dLookUp singleton until after it is constructed
float is_after_cut_value
default selection for the ordering of the segment pair
basf2 segment_finder_module
Use the SegmentFinderFacetAutomaton for track-segment creation with MC truth-matching.
basf2 segment_pair_finder_module
use the TrackFinderSegmentPairAutomaton for track-segment finding
None output_file_name
There is no default for the name of the output TFile.
Definition: mixins.py:60
Definition: main.py:1