Belle II Software  release-05-01-25
harvester.py
1 from tracking.validation.harvesting import HarvestingModule
2 from tracking.run.event_generation import MinimalRun
3 from tracking.validation import refiners
4 from ROOT import Belle2
5 import numpy as np
6 import ROOT
7 from tracking.validation.pr_side_module import PRSideTrackingValidationModule
8 from tracking.validation.module import TrackingValidationModule
9 
10 from tracking.ipython_tools.wrapper import QueueHarvester
11 
12 
14 
15  """ The base class with all static methods to use. """
16 
17  @staticmethod
18  def do_for_each_hit_type(cluster, svd_function, pxd_function):
19  """Apply a PXD selection to a PXDCluster or an SVD selection to an SVDCluster"""
20  cluster_type = cluster.__class__.__name__
21  if cluster_type == "Belle2::PXDCluster":
22  return pxd_function(cluster)
23  elif cluster_type == "Belle2::SVDCluster":
24  return svd_function(cluster)
25  else:
26  raise TypeError("Unknown hit type")
27 
28  @staticmethod
30  """Return lists of charges and path lengths for the clusters associated with an MCParticle"""
31  charge_list = []
32  path_length_list = []
33 
34  tools = Belle2.VXDMomentumEstimationTools(clusters[0].__class__.__name__).getInstance()
35 
36  momentum = mc_particle.getMomentum()
37  position = mc_particle.getProductionVertex()
38  charge = mc_particle.getCharge()
39  b_field = Belle2.BFieldManager.getField(position).Z() / Belle2.Unit.T
40  helix = Belle2.Helix(position, momentum, charge, b_field)
41 
42  for cluster in clusters:
43 
44  charge = tools.getCalibratedCharge(cluster)
45  path_length = tools.getPathLength(cluster, helix)
46 
47  charge_list.append(calibrated_charge)
48  path_length_list.append(path_length)
49 
50  return charge_list, path_length_list, list(np.divide(charge_list, path_length_list))
51 
52  @staticmethod
53  def generate_truncated(charge_list):
54  """Sort then truncate a list to all but the last 2 entries or the first 4 entries or the first 6 entries"""
55  sorted_list = sorted(charge_list)
56  if len(sorted_list) > 2:
57  return sorted_list[:-2], sorted_list[:4], sorted_list[:6]
58  else:
59  return sorted_list, sorted_list[:4], sorted_list[:6]
60 
61 
63 
64  """ A harvester to check for the positions of the track points in the MCParticleTrajectories"""
65 
66  def __init__(self):
67  """Constructor"""
68  HarvestingModule.__init__(self, foreach="MCParticleTrajectorys", output_file_name="mc_trajectory.root")
69 
70  def peel(self, mc_particle_trajectory):
71  """Aggregate track-point position information for the trajectory"""
72  for track_point in mc_particle_trajectory:
73  yield {"x": track_point.x, "y": track_point.y, "z": track_point.z, "index": self.counter}
74 
75 
77  save_tree = refiners.SaveTreeRefiner()
78 
79 
81 
82  """ A harvester to redo parts of the analysis in the Belle II Paper by Robert """
83 
84  def __init__(self):
85  """Constructor"""
86  HarvestingModule.__init__(self, foreach="MCParticles", output_file_name="mc_particle.root")
87 
88  def pick(self, mc_particle):
89  """Select the MCParticle if it is a primary pion and has some PXD and/or SVD clusters"""
90  pxd_clusters = mc_particle.getRelationsFrom("PXDClusters")
91  svd_clusters = mc_particle.getRelationsFrom("SVDClusters")
92  return (mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle) and
93  abs(mc_particle.getPDG()) == 211 and
94  len(pxd_clusters) + len(svd_clusters) > 0)
95 
96  def generate_cluster_dicts(self, charge_list, path_length_list, normalized_charge_list, name):
97  """Create a dictionary from the lists of charges, normalized charges, and path lengths of the
98  clusters associated with an MCParticle"""
99  result = dict()
100 
101  truncated, first4, first6 = VXDMomentumEnergyEstimator.generate_truncated(normalized_charge_list)
102 
103  result.update({"sum_%s_charges" % name: sum(charge_list)})
104  result.update({"mean_%s_charges" % name: np.mean(charge_list)})
105  result.update({"sum_%s_normalized_charges" % name: sum(normalized_charge_list)})
106  result.update({"mean_%s_normalized_charges" % name: np.mean(normalized_charge_list)})
107 
108  result.update({"sum_%s_normalized_charges_truncated" % name: sum(truncated)})
109  result.update({"mean_%s_normalized_charges_truncated" % name: np.mean(truncated)})
110  result.update({"sum_%s_normalized_charges_first4" % name: sum(first4)})
111  result.update({"mean_%s_normalized_charges_first4" % name: np.mean(first4)})
112  result.update({"sum_%s_normalized_charges_first6" % name: sum(first6)})
113  result.update({"mean_%s_normalized_charges_first6" % name: np.mean(first6)})
114 
115  return result
116 
117  def peel(self, mc_particle):
118  """Aggregate the PXD and SVD cluster information for an MCParticle"""
119 
120  pxd_clusters = mc_particle.getRelationsFrom("PXDClusters")
121  svd_clusters = mc_particle.getRelationsFrom("SVDClusters")
122 
123  pxd_results = VXDMomentumEnergyEstimator.calculate_charges_and_path_lengths_for_one_type(pxd_clusters, mc_particle)
124  svd_results = VXDMomentumEnergyEstimator.calculate_charges_and_path_lengths_for_one_type(svd_clusters, mc_particle)
125 
126  pxd_cluster_dicts = self.generate_cluster_dicts(*pxd_results, name="pxd")
127  pxd_charges, pxd_path_length, pxd_normalized_charges = pxd_results
128 
129  svd_charges, svd_path_length, svd_normalized_charges = svd_results
130  svd_cluster_dicts = self.generate_cluster_dicts(*svd_results, name="svd")
131 
132  combined_cluster_dicts = self.generate_cluster_dicts(pxd_charges + svd_charges,
133  pxd_path_length + svd_path_length,
134  pxd_normalized_charges + svd_normalized_charges,
135  name="combined")
136 
137  result.update(pxd_cluster_dicts)
138  result.update(svd_cluster_dicts)
139  result.update(combined_cluster_dicts)
140 
141  return result
142 
143 
145  save_tree = refiners.SaveTreeRefiner()
146 
147 
148 class VXDHarvester(QueueHarvester):
149 
150  """ A base class for the VXD hitwise analysis. Collect dE/dX and the correct p of each hit of the MC particles. """
151 
152  def __init__(self, clusters, detector, output_file_name, use_mc_info=True):
153  """Constructor"""
154  HarvestingModule.__init__(self, foreach="TrackCands", output_file_name=output_file_name)
155 
156 
157  self.svd_tools = Belle2.VXDMomentumEstimationTools("Belle2::SVDCluster").getInstance()
158 
159  self.pxd_tools = Belle2.VXDMomentumEstimationTools("Belle2::PXDCluster").getInstance()
160 
161 
162  self.clusters = clusters
163 
164  self.detector = detector
165 
166 
167  self.use_mc_info = use_mc_info
168 
169  def is_valid_cluster(self, cluster):
170  """Determine if a cluster has an associated SpacePoint and is associated with a primary pion MCParticle"""
171  mc_particles = cluster.getRelationsTo("MCParticles")
172 
173  space_point = cluster.getRelated("SpacePoints")
174 
175  if space_point is None:
176  return False
177 
178  for mc_particle in mc_particles:
179  if (mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle) and
180  abs(mc_particle.getPDG()) == 211):
181 
182  return True
183 
184  return False
185 
186  def get_tools(self, cluster):
187  """Get the PXD tools for a PXD cluster or the SVD tools for an SVD cluster"""
188  return VXDMomentumEnergyEstimator.do_for_each_hit_type(
189  cluster,
190  lambda cluster: self.svd_tools,
191  lambda cluster: self.pxd_tools)
192 
193  def peel(self, track_cand):
194  """Aggregate the cluster and MCParticle (for primary pion) information associated with the track candidate"""
195  vxd_hit_ids = track_cand.getHitIDs(self.detector)
196 
197  vxd_hits = Belle2.PyStoreArray(self.clusters)
198 
199  for vxd_hit_id in vxd_hit_ids:
200  cluster = vxd_hits[vxd_hit_id]
201 
202  if not self.is_valid_cluster(cluster):
203  continue
204 
205  tools = self.get_tools(cluster)
206 
207  mc_particles = cluster.getRelationsTo("MCParticles")
208 
209  for mc_particle in mc_particles:
210  if (mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle) and abs(mc_particle.getPDG()) == 211):
211 
212  if self.use_mc_info:
213  track_momentum = mc_particle.getMomentum()
214  track_position = mc_particle.getProductionVertex()
215  track_charge = mc_particle.getCharge()
216  else:
217  track_momentum = track_cand.getMomSeed()
218  track_position = track_cand.getPosSeed()
219  track_charge = track_cand.getChargeSeed()
220 
221  b_field = Belle2.BFieldManager.getField(track_position).Z() / Belle2.Unit.T
222  track_helix = Belle2.Helix(track_position, track_momentum, int(track_charge), b_field)
223 
224  cluster_charge = tools.getCalibratedCharge(cluster)
225  path_length = tools.getPathLength(cluster, track_helix)
226  cluster_thickness = tools.getThicknessOfCluster(cluster)
227  cluster_radius = tools.getRadiusOfCluster(cluster)
228  cluster_width = tools.getWidthOfCluster(cluster)
229  cluster_length = tools.getLengthOfCluster(cluster)
230 
231  perp_s_at_cluster_entry = track_helix.getArcLength2DAtCylindricalR(cluster_radius)
232  perp_s_at_cluster_exit = track_helix.getArcLength2DAtCylindricalR(cluster_radius + cluster_thickness)
233 
234  mc_momentum = tools.getEntryMomentumOfMCParticle(cluster)
235  mc_position = tools.getEntryPositionOfMCParticle(cluster)
236 
237  mc_b_field = Belle2.BFieldManager.getField(mc_position).Z() / Belle2.Unit.T
238  mc_helix = Belle2.Helix(mc_position, mc_momentum, int(track_charge), mc_b_field)
239  mc_path_length = tools.getPathLength(cluster, mc_helix)
240 
241  cluster_is_u = VXDMomentumEnergyEstimator.do_for_each_hit_type(
242  cluster,
243  lambda cluster: cluster.isUCluster(),
244  lambda cluster: np.NaN)
245 
246  cluster_is_pxd = VXDMomentumEnergyEstimator.do_for_each_hit_type(
247  cluster,
248  lambda cluster: False,
249  lambda cluster: True)
250 
251  cluster_layer = tools.getLayerOfCluster(cluster)
252  cluster_segment = tools.getSegmentNumberOfCluster(cluster)
253  cluster_ladder = tools.getLadderOfCluster(cluster)
254  cluster_sensor = tools.getSensorNumberOfCluster(cluster)
255 
256  cluster_dict = dict(cluster_charge=cluster_charge,
257  cluster_thickness=cluster_thickness,
258  cluster_radius=cluster_radius,
259  cluster_is_u=cluster_is_u,
260  cluster_is_pxd=cluster_is_pxd,
261  cluster_layer=cluster_layer,
262  cluster_segment=cluster_segment,
263  cluster_ladder=cluster_ladder,
264  cluster_width=cluster_width,
265  cluster_length=cluster_length,
266  cluster_sensor=cluster_sensor)
267 
268  mc_at_hit_dict = dict(mc_helix_perigee_x=mc_helix.getPerigeeX(),
269  mc_helix_perigee_y=mc_helix.getPerigeeY(),
270  mc_helix_perigee_z=mc_helix.getPerigeeZ(),
271  mc_helix_momentum_x=mc_helix.getMomentumX(mc_b_field),
272  mc_helix_momentum_y=mc_helix.getMomentumY(mc_b_field),
273  mc_helix_momentum_z=mc_helix.getMomentumZ(mc_b_field),
274  mc_position=mc_position.Mag(),
275  mc_position_x=mc_position.X(),
276  mc_position_y=mc_position.Y(),
277  mc_position_z=mc_position.Z(),
278  mc_momentum=mc_momentum.Mag(),
279  mc_momentum_x=mc_momentum.X(),
280  mc_momentum_y=mc_momentum.Y(),
281  mc_momentum_z=mc_momentum.Z())
282 
283  dedx_dict = dict(dedx=cluster_charge / path_length,
284  dedx_with_mc=cluster_charge / mc_path_length,
285  dedx_with_thickness=cluster_charge / cluster_thickness,
286  p=mc_momentum.Mag(),
287  perp_s_at_cluster_entry=perp_s_at_cluster_entry,
288  perp_s_at_cluster_exit=perp_s_at_cluster_exit,
289  track_charge=track_charge,
290  path_length=path_length,
291  mc_path_length=mc_path_length,
292  p_origin=mc_particle.getMomentum().Mag())
293 
294  track_dict = dict(track_helix_perigee_x=track_helix.getPerigeeX(),
295  track_helix_perigee_y=track_helix.getPerigeeY(),
296  track_helix_perigee_z=track_helix.getPerigeeZ(),
297  track_helix_momentum_x=track_helix.getMomentumX(b_field),
298  track_helix_momentum_y=track_helix.getMomentumY(b_field),
299  track_helix_momentum_z=track_helix.getMomentumZ(b_field))
300 
301  result_dict = dict()
302  result_dict.update(cluster_dict)
303  result_dict.update(mc_at_hit_dict)
304  result_dict.update(dedx_dict)
305  result_dict.update(track_dict)
306 
307  yield result_dict
308 
309 
311  save_tree = refiners.SaveTreeRefiner()
312 
313 
315  """Collect dE/dX and the correct p of each PXD hit of the MC particles. """
316 
317  def __init__(self, output_file_name, use_mc_info):
318  """Constructor"""
319  VXDHarvester.__init__(self, clusters="PXDClusters", detector=Belle2.Const.PXD, output_file_name=output_file_name,
320  use_mc_info=use_mc_info)
321 
322 
324  """Collect dE/dX and the correct p of each SVD hit of the MC particles. """
325 
326  def __init__(self, output_file_name, use_mc_info):
327  """Constructor"""
328  VXDHarvester.__init__(self, clusters="SVDClusters", detector=Belle2.Const.SVD, output_file_name=output_file_name,
329  use_mc_info=use_mc_info)
330 
331 
332 class FitHarvester(QueueHarvester):
333  """Collect dE/dX and the correct p of each VXD hit associated with the fitted tracks. """
334 
335  def __init__(self, output_file_name, queue):
336  """Constructor"""
337  QueueHarvester.__init__(self, queue, foreach="TrackFitResults", output_file_name=output_file_name)
338 
340 
341  def pick(self, track_fit_result):
342  """Select a TrackFitResult if it is associated with exactly one MCParticle"""
343  mc_track_cands = track_fit_result.getRelationsFrom("TrackCands")
344  if len(mc_track_cands) != 1:
345  return False
346 
347  mc_track_cand = mc_track_cands[0]
348  mc_particles = self.data_store.getRelationsFromObj(mc_track_cand, "MCParticles")
349 
350  return len(mc_particles) == 1
351 
352  def peel(self, track_fit_result):
353  """Aggregate the track-fit information associated with a TrackFitResult"""
354  mc_track_cand = track_fit_result.getRelationsFrom("TrackCands")[0]
355  mc_particle = self.data_store.getRelated(mc_track_cand, "MCParticles")
356 
357  fit_momentum = track_fit_result.getMomentum()
358  true_momentum = mc_particle.getMomentum()
359 
360  related_reco_track = track_fit_result.getRelated("GF2Tracks")
361  cardinal_rep = related_reco_track.getCardinalRep()
362  kalman_fit_state = related_reco_track.getKalmanFitStatus()
363 
364  number_of_measurements_in_total = 0
365  number_of_measurements_with_smaller_weight = 0
366 
367  number_of_momentum_measurements_in_total = 0
368  number_of_momentum_measurements_with_smaller_weight = 0
369 
370  for track_point_ID in range(related_reco_track.getNumPointsWithMeasurement()):
371  track_point = related_reco_track.getPointWithMeasurement(track_point_ID)
372 
373  is_momentum_measurement = track_point.getRawMeasurement().__class__.__name__ == "genfit::PlanarMomentumMeasurement"
374 
375  if is_momentum_measurement:
376  number_of_momentum_measurements_in_total += 1
377 
378  if track_point.hasFitterInfo(cardinal_rep):
379  fitter_info = track_point.getFitterInfo(cardinal_rep)
380  num_measurements = fitter_info.getNumMeasurements()
381 
382  for measurement_id in range(num_measurements):
383  number_of_measurements_in_total += 1
384  weight = fitter_info.getMeasurementOnPlane(measurement_id).getWeight()
385  if weight != 1:
386  number_of_measurements_with_smaller_weight += 1
387 
388  if is_momentum_measurement:
389  number_of_momentum_measurements_with_smaller_weight += 1
390 
391  return dict(fit_momentum_x=fit_momentum.X(),
392  fit_momentum_y=fit_momentum.Y(),
393  fit_momentum_z=fit_momentum.Z(),
394  p_value=kalman_fit_state.getForwardPVal(),
395  backward_p_value=kalman_fit_state.getBackwardPVal(),
396  true_momentum_x=true_momentum.X(),
397  true_momentum_y=true_momentum.Y(),
398  true_momentum_z=true_momentum.Z(),
399  number_of_measurements_in_total=number_of_measurements_in_total,
400  number_of_measurements_with_smaller_weight=number_of_measurements_with_smaller_weight,
401  number_of_momentum_measurements_in_total=number_of_momentum_measurements_in_total,
402  number_of_momentum_measurements_with_smaller_weight=number_of_momentum_measurements_with_smaller_weight)
403 
404 
406  save_tree = refiners.SaveTreeRefiner()
harvester.VXDHarvester.clusters
clusters
cached copy of the name of the cluster StoreArray
Definition: harvester.py:162
harvester.FitHarvester.peel
def peel(self, track_fit_result)
Definition: harvester.py:352
harvester.VXDMomentumEnergyEstimator.do_for_each_hit_type
def do_for_each_hit_type(cluster, svd_function, pxd_function)
Definition: harvester.py:18
harvester.MCParticleHarvester.generate_cluster_dicts
def generate_cluster_dicts(self, charge_list, path_length_list, normalized_charge_list, name)
Definition: harvester.py:96
Belle2::BFieldManager::getField
static void getField(const double *pos, double *field)
return the magnetic field at a given position.
Definition: BFieldManager.h:110
harvester.MCParticleHarvester.peel
def peel(self, mc_particle)
Definition: harvester.py:117
Belle2::DataStore::Instance
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
harvester.VXDHarvester.get_tools
def get_tools(self, cluster)
Definition: harvester.py:186
harvester.MCTrajectoryHarvester.__init__
def __init__(self)
Definition: harvester.py:66
tracking.validation.module
Definition: module.py:1
harvester.FitHarvester.__init__
def __init__(self, output_file_name, queue)
Definition: harvester.py:335
harvester.MCTrajectoryHarvester.peel
def peel(self, mc_particle_trajectory)
Definition: harvester.py:70
harvester.MCParticleHarvester.__init__
def __init__(self)
Definition: harvester.py:84
harvester.VXDHarvester.svd_tools
svd_tools
cached accessor to the SVD-tools singleton
Definition: harvester.py:157
harvester.FitHarvester
Definition: harvester.py:332
harvester.SVDHarvester
Definition: harvester.py:323
harvester.VXDHarvester.__init__
def __init__(self, clusters, detector, output_file_name, use_mc_info=True)
Definition: harvester.py:152
harvester.MCParticleHarvester
Definition: harvester.py:80
harvester.PXDHarvester
Definition: harvester.py:314
harvester.VXDHarvester.detector
detector
cached copy of the detector identifier (PXD or SVD)
Definition: harvester.py:164
harvester.VXDHarvester.peel
def peel(self, track_cand)
Definition: harvester.py:193
harvester.VXDHarvester.is_valid_cluster
def is_valid_cluster(self, cluster)
Definition: harvester.py:169
harvester.MCParticleHarvester.pick
def pick(self, mc_particle)
Definition: harvester.py:88
harvester.MCTrajectoryHarvester
Definition: harvester.py:62
harvester.VXDMomentumEnergyEstimator.generate_truncated
def generate_truncated(charge_list)
Definition: harvester.py:53
Belle2::CDC::Helix
Helix parameter class.
Definition: Helix.h:51
tracking.run.event_generation
Definition: event_generation.py:1
harvester.SVDHarvester.__init__
def __init__(self, output_file_name, use_mc_info)
Definition: harvester.py:326
Belle2::PyStoreArray
a (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:58
tracking.validation
Definition: __init__.py:1
harvester.VXDMomentumEnergyEstimator.calculate_charges_and_path_lengths_for_one_type
def calculate_charges_and_path_lengths_for_one_type(clusters, mc_particle)
Definition: harvester.py:29
harvester.FitHarvester.data_store
data_store
access the DataStore singletion
Definition: harvester.py:339
harvester.VXDHarvester.use_mc_info
use_mc_info
if true the MC information is used
Definition: harvester.py:167
harvester.PXDHarvester.__init__
def __init__(self, output_file_name, use_mc_info)
Definition: harvester.py:317
tracking.harvest.harvesting.HarvestingModule
Definition: harvesting.py:86
harvester.VXDHarvester
Definition: harvester.py:148
Belle2::VXDMomentumEstimationTools
Tools needed for the VXD momentum estimation to, e.g.
Definition: VXDMomentumEstimationTools.h:43
harvester.VXDHarvester.pxd_tools
pxd_tools
cached accessor to the PXD-tools singleton
Definition: harvester.py:159
harvester.FitHarvester.pick
def pick(self, track_fit_result)
Definition: harvester.py:341
harvester.VXDMomentumEnergyEstimator
Definition: harvester.py:13