Belle II Software  release-05-01-25
record.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 import basf2
5 
6 import ROOT
7 from ROOT import Belle2 # make Belle2 namespace available
8 from ROOT import std
9 
10 import os
11 import sys
12 
13 from tracking.validation.utilities import is_primary
14 from tracking.validation.plot import ValidationPlot
15 
16 import tracking.harvest.harvesting as harvesting
17 import tracking.harvest.refiners as refiners
18 from tracking.harvest.run import HarvestingRun
19 
20 import trackfindingcdc.harvest.cdc_peelers as cdc_peelers
21 
22 import logging
23 
24 
25 def get_logger():
26  return logging.getLogger(__name__)
27 
28 
29 CONTACT = "oliver.frost@desy.de"
30 
31 
33  """Harvester to generate, postprocess and inspect MC events for track-segment-pair fit validation"""
34 
35  n_events = 10000
36 
37  generator_module = "simple_gun" # Rather high momentum tracks should make the tracks rather straight.
38 
39 
40  monte_carlo = "no"
41 
42  segment_orientation = "outwards"
43 
44 
45  fit_method_name = "fuse-sz"
46 
47  @property
48  def output_file_name(self):
49  """Get the output ROOT filename"""
50  file_name = self.fit_method_name
51  file_name += "-mc-" + self.monte_carlo
52 
53  if self.root_input_file:
54  file_name += "-" + os.path.split(self.root_input_file)[1]
55  else:
56  file_name += ".root"
57 
58  return file_name
59 
60  def harvesting_module(self, path=None):
61  """Harvest and post-process the MC events"""
62  harvesting_module = SegmentPairFitValidationModule(self.output_file_name)
63  if path:
64  path.add_module(harvesting_module)
65  return harvesting_module
66 
67  def create_argument_parser(self, **kwds):
68  """Convert command-line arguments to basf2 argument list"""
69  argument_parser = super().create_argument_parser(**kwds)
70 
71  argument_parser.add_argument(
72  '-m',
73  '--monte-carlo',
74  choices=["no", "medium", "full"],
75  default=self.monte_carlo,
76  dest='monte_carlo',
77  help='Amount of monte carlo information to be used in the segment generation.',
78  )
79 
80  argument_parser.add_argument(
81  "--fit",
82  choices=["zreco", "fuse-pre", "fuse-sz", "fuse-sz-re"],
83  default=self.fit_method_name,
84  dest="fit_method_name",
85  help=("Choose which fit positional information of the segment should be used. \n"
86  "* 'zreco' means the z coordinate is reconstructed and a linear sz fit is made. "
87  "No covariance between the circle and the linear sz part can be made.\n"
88  "* 'fuse-sz' means the Kalmanesk fuse of the two trajectory fits.\n"
89  "* 'fuse-sz-re' means the Kalmanesk fuse of the two trajectory fits but reestimate the drift length."
90  )
91  )
92 
93  return argument_parser
94 
95  def get_fit_method(self):
96  """Determine which track-segment-pair fitter to use"""
97  fit_method_name = self.fit_method_name
98 
99  if fit_method_name == 'zreco':
101 
102  def z_reconstruction_fit(pair):
103  return sz_fitter.update(pair)
104  return z_reconstruction_fit
105 
106  elif fit_method_name.startswith('fuse-pre'):
107  CDCAxialStereoFusion = Belle2.TrackFindingCDC.CDCAxialStereoFusion
109  fusionFit = CDCAxialStereoFusion()
110 
111  def sz_segment_pair_preliminary_fit(pair):
112  fusionFit.fusePreliminary(pair)
113  return sz_segment_pair_preliminary_fit
114 
115  elif fit_method_name.startswith('fuse-sz'):
116  CDCAxialStereoFusion = Belle2.TrackFindingCDC.CDCAxialStereoFusion
118  reestimateDriftLength = fit_method_name.endswith("re")
119  fusionFit = CDCAxialStereoFusion(reestimateDriftLength)
120 
121  def sz_segment_pair_fusion_fit(pair):
122  fusionFit.reconstructFuseTrajectories(pair)
123  return
124 
125  trajectory3D = pair.getTrajectory3D()
126  revFromSegment = pair.getFromSegment().reversed()
127  revToSegment = pair.getToSegment().reversed()
128  revPair = CDCSegmentPair(revToSegment, revFromSegment)
129 
130  CDCAxialStereoFusion.reconstructFuseTrajectories(revPair)
131  revTrajectory3D = revPair.getTrajectory3D().reversed()
132 
133  # print("One origin x", trajectory3D.getLocalOrigin().x())
134  # print("One origin y", trajectory3D.getLocalOrigin().y())
135  # print("One origin z", trajectory3D.getLocalOrigin().z())
136 
137  # print("Rev origin x", revTrajectory3D.getLocalOrigin().x())
138  # print("Rev origin y", revTrajectory3D.getLocalOrigin().y())
139  # print("Rev origin z", revTrajectory3D.getLocalOrigin().z())
140 
141  print("One parameters", [trajectory3D.getLocalHelix().parameters()[i] for i in range(5)])
142  print("Rev parameters", [revTrajectory3D.getLocalHelix().parameters()[i] for i in range(5)])
143 
144  print("One covariance")
145  for j in range(5):
146  print([trajectory3D.getLocalHelix().helixCovariance()(i, j) for i in range(5)])
147 
148  print("Rev covariance")
149  for j in range(5):
150  print([revTrajectory3D.getLocalHelix().helixCovariance()(i, j) for i in range(5)])
151 
152  # return revTrajectory3D
153  # return trajectory3D
154 
155  return sz_segment_pair_fusion_fit
156 
157  else:
158  raise ValueError("Unexpected fit_positions %s" % fit_method_name)
159 
160  def create_path(self):
161  """
162  Sets up a path that plays back pregenerated events or generates events
163  based on the properties in the base class.
164  """
165  path = super().create_path()
166 
167  path.add_module("TFCDC_WireHitPreparer",
168  flightTimeEstimation="outwards",
169  UseNLoops=0.5
170  )
171 
172  path.add_module("TFCDC_ClusterPreparer")
173 
174 
175  if self.monte_carlo == "no":
176  # MC free - default
177  path.add_module("TFCDC_SegmentFinderFacetAutomaton",
178  SegmentOrientation="outwards"
179  )
180 
181  path.add_module("TFCDC_SegmentFitter",
182  inputSegments="CDCSegment2DVector",
183  updateDriftLength=True,
184  useAlphaInDriftLength=True,
185  )
186 
187  elif self.monte_carlo == "medium":
188  # Medium MC - proper generation logic, but true facets and facet relations
189  path.add_module("TFCDC_SegmentFinderFacetAutomaton",
190  FacetFilter="truth",
191  FacetRelationFilter="truth",
192  SegmentOrientation="outwards"
193  )
194 
195  path.add_module("TFCDC_SegmentFitter",
196  inputSegments="CDCSegment2DVector",
197  updateDriftLength=True,
198  useAlphaInDriftLength=True,
199  )
200 
201  elif self.monte_carlo == "full":
202  # Only true monte carlo segments
203  # make the positions realistic but keep the true drift length
204  path.add_module("TFCDC_SegmentCreatorMCTruth",
205  reconstructedDriftLength=False,
206  reconstructedPositions=True,
207  # segments="MCSegments"
208  )
209 
210  path.add_module("TFCDC_SegmentFitter",
211  inputSegments="CDCSegment2DVector",
212  updateDriftLength=False,
213  # useAlphaInDriftLength=True,
214  )
215 
216  else:
217  raise ValueError("Invalid degree of Monte Carlo information")
218 
219  path.add_module("TFCDC_SegmentOrienter",
220  SegmentOrientation="outwards",
221  # SegmentOrientation="none",
222  inputSegments="CDCSegment2DVector",
223  segments="CDCSegment2DVectorOriented"
224  )
225 
226  path.add_module("TFCDC_TrackFinderSegmentPairAutomaton",
227  inputSegments="CDCSegment2DVectorOriented",
228  WriteSegmentPairs=True,
229  SegmentPairFilter="truth",
230  SegmentPairFilterParameters={"allowReverse": True},
231  SegmentPairRelationFilter="none"
232  )
233 
234  path.add_module(AxialStereoPairFitterModule(fit_method=self.get_fit_method()))
235  return path
236 
237 
238 class SegmentPairFitValidationModule(harvesting.HarvestingModule):
239 
240  """Module to collect information about the generated segment pairs and
241  compose validation plots on terminate."""
242 
243  def __init__(self, output_file_name):
244  """Constructor"""
245  super(SegmentPairFitValidationModule, self).__init__(
246  output_file_name=output_file_name,
247  foreach="CDCSegmentPairVector"
248  )
249 
250 
251  self.mc_segment_lookup = None
252 
253  def initialize(self):
254  """Receive signal at the start of event processing"""
256  super(SegmentPairFitValidationModule, self).initialize()
257 
258  def prepare(self):
259  """Initialize the MC-hit lookup method"""
261 
262  def pick(self, segment_pair):
263  """Select segment pairs with 4 or more hits per segment and a matching primary MC particle"""
264  mc_segment_lookup = self.mc_segment_lookup
265  from_segment = segment_pair.getFromSegment()
266  to_segment = segment_pair.getToSegment()
267  mc_particle = mc_segment_lookup.getMCParticle(from_segment)
268  return (mc_particle and
269  is_primary(mc_particle) and
270  from_segment.size() > 3 and
271  to_segment.size() > 3)
272 
273  def peel(self, segment_pair):
274  """Aggregate the track and MC information for track-segment analysis"""
275  mc_segment_lookup = self.mc_segment_lookup
276 
277  from_segment = segment_pair.getFromSegment()
278  to_segment = segment_pair.getToSegment()
279 
280  mc_particle = mc_segment_lookup.getMCParticle(from_segment)
281 
282  # Take the fit best at the middle of the segment pair
283  fit3d_truth = mc_segment_lookup.getTrajectory3D(to_segment)
284 
285  fb_info = 1 if segment_pair.getAutomatonCell().getCellWeight() > 0 else -1
286  truth_crops = dict(
287  curvature_truth=fb_info * fit3d_truth.getCurvatureXY(),
288  tan_lambda_truth=fb_info * fit3d_truth.getTanLambda(),
289  )
290 
291  segment_pair_crops = cdc_peelers.peel_segment_pair(segment_pair)
292  segment_pair_crops.update(truth_crops)
293  return segment_pair_crops
294 
295  # Refiners to be executed at the end of the harvesting / termination of the module
296 
297  save_histograms = refiners.save_histograms(outlier_z_score=5.0, allow_discrete=True)
298 
299  save_tree = refiners.save_tree()
300 
301 
302  save_curvature_pull_aux = refiners.save_pull_analysis(
303  part_name="curvature",
304  folder_name="aux",
305  unit="1/cm",
306  absolute=False,
307  aux_names=["superlayer_id_pair", "tan_lambda_truth"],
308  which_plots=["aux"],
309  outlier_z_score=3.0)
310 
311 
312  save_curvature_pull = refiners.save_pull_analysis(
313  part_name="curvature",
314  unit="1/cm",
315  absolute=False,
316  groupby=[None, "superlayer_id_pair"],
317  outlier_z_score=3.0)
318 
319 
320  save_absolute_curvature_pull = refiners.save_pull_analysis(
321  part_name="curvature",
322  unit="1/cm",
323  absolute=True,
324  groupby=[None, "superlayer_id_pair"],
325  outlier_z_score=3.0)
326 
327 
328  save_tan_lambda_pull = refiners.save_pull_analysis(
329  part_name="tan_lambda",
330  quantity_name="tan #lambda",
331  absolute=False,
332  groupby=[None, "superlayer_id_pair"],
333  outlier_z_score=3.0)
334 
335 
336  save_fit_quality_histograms = refiners.save_histograms(
337  outlier_z_score=5.0,
338  select={"ndf": "ndf",
339  "chi2": "#chi2",
340  "p_value": "p-value",
341  },
342  groupby=[None, "superlayer_id_pair"],
343  description="Distribution of {part_name} in the segment fits",
344  )
345 
346 
347  save_fit_quality_by_tan_lambda_profiles = refiners.save_profiles(
348  select={
349  "p_value": "fit p-value",
350  "tan_lambda_truth": "true tan #lambda",
351  },
352  groupby=[None, "superlayer_id_pair"],
353  x="true tan #lambda",
354  y="fit p-value",
355  check=("The {y_part_name} should be essentially the same over"
356  "the whole range of the tan lambda spectrum"),
357  description=("Investigating the reconstruction quality for different "
358  "tan lambda regions of the CDC. Most notably is the superlayer dependency."
359  "For stereo superlayers the curve is not flat but has distinct slope."),
360  fit='line',
361  )
362 
363 
364 class AxialStereoPairFitterModule(basf2.Module):
365  """Fit axial and stereo track segment pairs"""
366 
367  @staticmethod
368  def default_fit_method(segmentPair):
369  """Default method to fit the generated segment pairs."""
370 
371  CDCAxialStereoFusion = Belle2.TrackFindingCDC.CDCAxialStereoFusion
372  CDCAxialStereoFusion.reconstructFuseTrajectories(segmentPair,
373  True)
374 
375  def __init__(self, fit_method=None):
376  """Constructor"""
377 
378 
382  self.fit_method = fit_method
383  if not fit_method:
384  self.fit_method = self.default_fit_method
385 
386  super(AxialStereoPairFitterModule, self).__init__()
387 
388  def event(self):
389  """Called by basf2 for each event"""
390  self.fitStoredPairs()
391 
392  def fitStoredPairs(self):
393  """Fits all pairs in the StoreArray with designated fit method."""
394 
395  fit_method = self.fit_method
396 
397  stored_segment_pair_relations = Belle2.PyStoreObj("CDCSegmentPairVector")
398  wrapped_segment_pair_relations = stored_segment_pair_relations.obj()
399  segment_pair_relations = wrapped_segment_pair_relations.get()
400 
401  for segment_pair_relation in segment_pair_relations:
402  fit_method(segment_pair_relation)
403 
404 
405 def main():
407  run.configure_and_execute_from_commandline()
408 
409 
410 if __name__ == "__main__":
411  logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(levelname)s:%(message)s')
412  main()
record.AxialStereoPairFitterModule
Definition: record.py:364
record.SegmentPairFitValidationRun.create_argument_parser
def create_argument_parser(self, **kwds)
Definition: record.py:67
Belle2::TrackFindingCDC::CDCSegmentPair
Class representing a pair of one reconstructed axial segement and one stereo segment in adjacent supe...
Definition: CDCSegmentPair.h:44
Belle2::TrackFindingCDC::CDCSZFitter::getFitter
static const CDCSZFitter & getFitter()
Getter for a standard sz line fitter instance.
Definition: CDCSZFitter.cc:38
tracking.harvest.refiners
Definition: refiners.py:1
record.AxialStereoPairFitterModule.default_fit_method
def default_fit_method(segmentPair)
Definition: record.py:368
record.SegmentPairFitValidationRun.get_fit_method
def get_fit_method(self)
Definition: record.py:95
record.SegmentPairFitValidationModule.pick
def pick(self, segment_pair)
Definition: record.py:262
Belle2::PyStoreObj
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:69
tracking.harvest.harvesting
Definition: harvesting.py:1
record.SegmentPairFitValidationRun.create_path
def create_path(self)
Definition: record.py:160
record.SegmentPairFitValidationModule.__init__
def __init__(self, output_file_name)
Definition: record.py:243
Belle2::TrackFindingCDC::CDCMCSegment2DLookUp::getInstance
static const CDCMCSegment2DLookUp & getInstance()
Getter for the singletone instance.
Definition: CDCMCSegment2DLookUp.cc:23
record.SegmentPairFitValidationModule.mc_segment_lookup
mc_segment_lookup
by default, there is no method to find matching MC track segments
Definition: record.py:251
record.SegmentPairFitValidationRun.harvesting_module
def harvesting_module(self, path=None)
Definition: record.py:60
record.SegmentPairFitValidationModule.prepare
def prepare(self)
Definition: record.py:258
record.AxialStereoPairFitterModule.fit_method
fit_method
fit_method : function A function called on each stored segment pair as its only argument to update it...
Definition: record.py:382
tracking.harvest.run
Definition: run.py:1
record.SegmentPairFitValidationModule.peel
def peel(self, segment_pair)
Definition: record.py:273
main
int main(int argc, char **argv)
Run all tests.
Definition: test_main.cc:77
tracking.run.event_generation.ReadOrGenerateEventsRun.root_input_file
root_input_file
generating events, so there is no ROOT input file
Definition: event_generation.py:120
record.SegmentPairFitValidationRun.monte_carlo
string monte_carlo
do not generate new events
Definition: record.py:40
cdc_peelers.peel_segment_pair
def peel_segment_pair(segment_pair, key="{part_name}")
Definition: cdc_peelers.py:73
tracking.harvest.run.HarvestingRunMixin.output_file_name
output_file_name
Disable the writing of an output ROOT file.
Definition: run.py:12
record.SegmentPairFitValidationModule
Definition: record.py:238
Belle2::TrackFindingCDC::CDCAxialStereoFusion
Utility class implementing the Kalmanesk combination of to two dimensional trajectories to one three ...
Definition: CDCAxialStereoFusion.h:40
Belle2::TrackFindingCDC::CDCMCHitLookUp::getInstance
static const CDCMCHitLookUp & getInstance()
Getter for the singletone instance.
Definition: CDCMCHitLookUp.cc:32
record.SegmentPairFitValidationRun
Definition: record.py:32
tracking.validation.utilities
Definition: utilities.py:1
record.SegmentPairFitValidationRun.fit_method_name
string fit_method_name
use the Kalmanesk fuse of the two trajectory fits
Definition: record.py:45
tracking.harvest.run.HarvestingRun
Definition: run.py:69
record.AxialStereoPairFitterModule.fitStoredPairs
def fitStoredPairs(self)
Definition: record.py:392
record.SegmentPairFitValidationModule.initialize
def initialize(self)
Definition: record.py:253
tracking.validation.plot
Definition: plot.py:1
record.AxialStereoPairFitterModule.__init__
def __init__(self, fit_method=None)
Definition: record.py:375
record.AxialStereoPairFitterModule.event
def event(self)
Definition: record.py:388