Belle II Software  release-05-01-25
utilities.py
1 import basf2 # Import basf2 to make the Belle2 namespace available
2 import ROOT
3 from ROOT import Belle2
4 
5 import numpy as np
6 import math
7 import contextlib
8 
9 # Vectorised version of the error function for numpy arrays
10 try:
11  from scipy.special import erf
12 except ImportError:
13  # Minimal workaround that only relies on numpy and python 2.7
14  # erf as a vectorized function
15  # Will convert the incoming nparray to dtype 'object', if nan
16  # values are contained
17  # use numpy.asfarray(...) to convert back
18  erf_ufunc = np.frompyfunc(math.erf, 1, 1)
19 
20  def erf(*args):
21  result = erf_ufunc(*args)
22  return np.asfarray(result)
23 
24 
25 # Vectorised version of the Prob function as known from TMath for numpy arrays
26 
27 # Minimal workaround that only relies on numpy and python 2.7
28 # prob as a vectorized function
29 prob = np.frompyfunc(ROOT.TMath.Prob, 2, 1)
30 
31 
32 def is_primary(mc_particle):
33  """Indicates if the given MCParticle is primary.
34 
35  Parameters
36  ----------
37  mc_particle : Belle2.MCParticle
38  MCParticle to be checked"""
39 
40  return mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle)
41 
42 
43 def is_stable_in_generator(mc_particle):
44  """Indicates if the given MCParticle is stable in the generator
45 
46  Parameters
47  ----------
48  mc_particle : Belle2.MCParticle
49  MCParticle to be checked"""
50 
51  return mc_particle.hasStatus(Belle2.MCParticle.c_StableInGenerator)
52 
53 
54 def get_det_hit_ids(reco_track, det_ids=[Belle2.Const.PXD, Belle2.Const.SVD, Belle2.Const.CDC]):
55  """Retrieves the hit ids contained in the track for the given detector ids
56 
57  Parameters
58  ----------
59  reco_track : RecoTrack
60  Track
61  det_ids : list(int)
62  List of the detector ids for which the hit ids should be retrieved
63 
64  Returns
65  -------
66  set( (int, int) )
67  A set of pairs like (det_id, hit_id) representing the hit content of the track
68  """
69  det_hit_ids = set()
70  for det_id in det_ids:
71  if det_id == Belle2.Const.CDC:
72  hits = reco_track.getCDCHitList()
73  elif det_id == Belle2.Const.SVD:
74  hits = reco_track.getSVDHitList()
75  elif det_id == Belle2.Const.PXD:
76  hits = reco_track.getPXDHitList()
77  else:
78  raise ValueError("DET ID not known.")
79 
80  # Working around a bug in ROOT where you should not access empty std::vectors
81  if len(hits) != 0:
82  det_hit_ids |= set((det_id, hit.getArrayIndex()) for hit in hits)
83 
84  return det_hit_ids
85 
86 
87 def calc_ndf_from_det_hit_ids(det_hit_ids,
88  ndf_by_det_id={Belle2.Const.PXD: 2,
89  Belle2.Const.SVD: 1,
90  Belle2.Const.CDC: 1}):
91  """For a set of detector and hit ids calculate the total number of degrees of freedom
92 
93  Parameters
94  ----------
95  det_hit_ids : set( (int, int) )
96  A set of pairs like (det_id, hit_id) representing the hit content of a track
97  ndf_by_det_ids : dict(int=int)
98  A map from detector ids to the number of degrees of freedom one hit in this detector represents.
99 
100  Returns
101  -------
102  int
103  Total number of degrees of freedom represented by the hit set
104  """
105  return sum(ndf_by_det_id[det_id] for det_id, hit_id in det_hit_ids)
106 
107 
108 def calc_hit_efficiency(det_hit_ids,
109  mc_det_hit_ids,
110  ndf_by_det_id={Belle2.Const.PXD: 2,
111  Belle2.Const.SVD: 1,
112  Belle2.Const.CDC: 1}):
113  """Calculates the fraction of detector and hits ids in a reference (MC) set that are also
114  present in a reconstructed (PR) set.
115 
116  The ratio is given in terms of degrees of freedom the hits represent
117 
118  Parameters
119  ----------
120  det_hit_ids : set( (int, int) )
121  A set of pairs like (det_id, hit_id) representing the hit content of a reconstructed (PR) track
122  mc_det_hit_ids : set( (int, int) )
123  A set of pairs like (det_id, hit_id) representing the hit content of a reference (MC) track
124  ndf_by_det_ids : dict(int=int)
125  A map from detector ids to the number of degrees of freedom one hit in this detector represents.
126 
127  Returns
128  -------
129  float
130  Fraction of hits in the reference (MC) track that are also present in the reconstructed (PR) track
131  in terms of number of degrees of freedom.
132  """
133  common_det_hit_ids = det_hit_ids & mc_det_hit_ids
134  return np.divide(1.0 * calc_ndf_from_det_hit_ids(common_det_hit_ids),
135  calc_ndf_from_det_hit_ids(mc_det_hit_ids))
136 
137 
138 def getHelixFromMCParticle(mc_particle):
139  position = mc_particle.getVertex()
140  momentum = mc_particle.getMomentum()
141  charge_sign = (-1 if mc_particle.getCharge() < 0 else 1)
142  b_field = Belle2.BFieldManager.getField(position).Z() / Belle2.Unit.T
143 
144  seed_helix = Belle2.Helix(position, momentum, charge_sign, b_field)
145  return seed_helix
146 
147 
148 def getSeedTrackFitResult(reco_track):
149  position = reco_track.getPositionSeed()
150  momentum = reco_track.getMomentumSeed()
151  cartesian_covariance = reco_track.getSeedCovariance()
152  charge_sign = (-1 if reco_track.getChargeSeed() < 0 else 1)
153  # It does not matter, which particle we put in here, so we just use a pion
154  particle_type = Belle2.Const.pion
155  p_value = float('nan')
156  b_field = Belle2.BFieldManager.getField(position).Z() / Belle2.Unit.T
157  cdc_hit_pattern = 0
158  svd_hit_pattern = 0
159  # the value 0xFFFF will cause the TrackFitResult::getNDF() to return -1
160  ndf = 0xFFFF
161 
162  track_fit_result = Belle2.TrackFitResult(
163  position,
164  momentum,
165  cartesian_covariance,
166  charge_sign,
167  particle_type,
168  p_value,
169  b_field,
170  cdc_hit_pattern,
171  svd_hit_pattern,
172  ndf
173  )
174 
175  return track_fit_result
Belle2::BFieldManager::getField
static void getField(const double *pos, double *field)
return the magnetic field at a given position.
Definition: BFieldManager.h:110
Belle2::TrackFitResult
Values of the result of a track fit with a given particle hypothesis.
Definition: TrackFitResult.h:59
Belle2::CDC::Helix
Helix parameter class.
Definition: Helix.h:51