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